2018-09-26 05:52:13 -07:00
//! The `poh_recorder` module provides an object for synchronizing with Proof of History.
2018-11-05 09:47:41 -08:00
//! It synchronizes PoH, bank's register_tick and the ledger
2018-09-26 05:52:13 -07:00
//!
2019-02-24 08:59:49 -08:00
//! PohRecorder will send ticks or entries to a WorkingBank, if the current range of ticks is
//! within the specified WorkingBank range.
//!
//! For Ticks:
2019-10-16 12:53:11 -07:00
//! * new tick_height must be > WorkingBank::min_tick_height && new tick_height must be <= WorkingBank::max_tick_height
2019-02-24 08:59:49 -08:00
//!
//! For Entries:
2019-05-18 14:01:36 -07:00
//! * recorded entry must be >= WorkingBank::min_tick_height && entry must be < WorkingBank::max_tick_height
2019-02-24 08:59:49 -08:00
//!
2019-09-06 14:30:56 -07:00
pub use solana_sdk ::clock ::Slot ;
2021-06-04 08:23:06 -07:00
use {
crate ::poh_service ::PohService ,
2022-04-05 06:57:12 -07:00
crossbeam_channel ::{ unbounded , Receiver , RecvTimeoutError , SendError , Sender , TrySendError } ,
2021-06-04 08:23:06 -07:00
log ::* ,
2021-07-14 05:16:29 -07:00
solana_entry ::{ entry ::Entry , poh ::Poh } ,
2021-09-13 16:55:35 -07:00
solana_ledger ::{
blockstore ::Blockstore ,
genesis_utils ::{ create_genesis_config , GenesisConfigInfo } ,
leader_schedule_cache ::LeaderScheduleCache ,
} ,
2022-06-06 18:21:05 -07:00
solana_measure ::measure ,
2022-03-30 07:04:49 -07:00
solana_metrics ::poh_timing_point ::{ send_poh_timing_point , PohTimingSender , SlotPohTimingInfo } ,
2021-06-04 08:23:06 -07:00
solana_runtime ::bank ::Bank ,
solana_sdk ::{
clock ::NUM_CONSECUTIVE_LEADER_SLOTS , hash ::Hash , poh_config ::PohConfig , pubkey ::Pubkey ,
2022-01-26 16:44:41 -08:00
transaction ::VersionedTransaction ,
2021-06-04 08:23:06 -07:00
} ,
std ::{
cmp ,
sync ::{
atomic ::{ AtomicBool , Ordering } ,
2022-07-05 07:29:44 -07:00
Arc , Mutex , RwLock ,
2021-06-04 08:23:06 -07:00
} ,
time ::{ Duration , Instant } ,
} ,
thiserror ::Error ,
2021-04-14 10:07:21 -07:00
} ;
2018-09-26 05:52:13 -07:00
2020-02-26 13:35:50 -08:00
pub const GRACE_TICKS_FACTOR : u64 = 2 ;
pub const MAX_GRACE_SLOTS : u64 = 2 ;
2019-03-15 13:22:16 -07:00
2020-01-02 19:50:43 -08:00
#[ derive(Error, Debug, Clone) ]
2018-10-18 22:57:48 -07:00
pub enum PohRecorderError {
2020-01-02 19:50:43 -08:00
#[ error( " invalid calling object " ) ]
2018-10-25 14:56:21 -07:00
InvalidCallingObject ,
2020-01-02 19:50:43 -08:00
#[ error( " max height reached " ) ]
2018-10-18 22:57:48 -07:00
MaxHeightReached ,
2020-01-02 19:50:43 -08:00
#[ error( " min height not reached " ) ]
2019-02-19 16:17:36 -08:00
MinHeightNotReached ,
2020-01-02 19:50:43 -08:00
#[ error( " send WorkingBankEntry error " ) ]
SendError ( #[ from ] SendError < WorkingBankEntry > ) ,
2019-02-19 16:17:36 -08:00
}
2020-01-02 19:50:43 -08:00
type Result < T > = std ::result ::Result < T , PohRecorderError > ;
2019-09-18 12:16:22 -07:00
pub type WorkingBankEntry = ( Arc < Bank > , ( Entry , u64 ) ) ;
2021-09-17 16:55:53 -07:00
2022-02-11 00:07:45 -08:00
#[ derive(Clone) ]
2021-09-17 16:55:53 -07:00
pub struct BankStart {
pub working_bank : Arc < Bank > ,
pub bank_creation_time : Arc < Instant > ,
}
impl BankStart {
fn get_working_bank_if_not_expired ( & self ) -> Option < & Arc < Bank > > {
if Bank ::should_bank_still_be_processing_txs (
& self . bank_creation_time ,
self . working_bank . ns_per_slot ,
) {
Some ( & self . working_bank )
} else {
None
}
}
}
2019-03-03 16:44:06 -08:00
2022-06-23 12:37:38 -07:00
// Sends the Result of the record operation, including the index in the slot of the first
// transaction, if being tracked by WorkingBank
type RecordResultSender = Sender < Result < Option < usize > > > ;
2021-03-23 07:10:04 -07:00
pub struct Record {
pub mixin : Hash ,
2021-08-17 15:17:56 -07:00
pub transactions : Vec < VersionedTransaction > ,
2021-03-23 07:10:04 -07:00
pub slot : Slot ,
2022-06-23 12:37:38 -07:00
pub sender : RecordResultSender ,
2021-03-23 07:10:04 -07:00
}
impl Record {
pub fn new (
mixin : Hash ,
2021-08-17 15:17:56 -07:00
transactions : Vec < VersionedTransaction > ,
2021-03-23 07:10:04 -07:00
slot : Slot ,
2022-06-23 12:37:38 -07:00
sender : RecordResultSender ,
2021-03-23 07:10:04 -07:00
) -> Self {
Self {
mixin ,
transactions ,
slot ,
sender ,
}
}
}
pub struct TransactionRecorder {
// shared by all users of PohRecorder
2022-01-11 02:44:46 -08:00
pub record_sender : Sender < Record > ,
2021-04-14 10:07:21 -07:00
pub is_exited : Arc < AtomicBool > ,
2021-03-23 07:10:04 -07:00
}
impl Clone for TransactionRecorder {
fn clone ( & self ) -> Self {
2021-04-14 10:07:21 -07:00
TransactionRecorder ::new ( self . record_sender . clone ( ) , self . is_exited . clone ( ) )
2021-03-23 07:10:04 -07:00
}
}
impl TransactionRecorder {
2022-01-11 02:44:46 -08:00
pub fn new ( record_sender : Sender < Record > , is_exited : Arc < AtomicBool > ) -> Self {
2021-03-23 07:10:04 -07:00
Self {
// shared
record_sender ,
2021-04-14 10:07:21 -07:00
// shared
is_exited ,
2021-03-23 07:10:04 -07:00
}
}
2022-06-23 12:37:38 -07:00
// Returns the index of `transactions.first()` in the slot, if being tracked by WorkingBank
2021-03-23 07:10:04 -07:00
pub fn record (
& self ,
bank_slot : Slot ,
mixin : Hash ,
2021-08-17 15:17:56 -07:00
transactions : Vec < VersionedTransaction > ,
2022-06-23 12:37:38 -07:00
) -> Result < Option < usize > > {
2021-03-29 22:51:35 -07:00
// create a new channel so that there is only 1 sender and when it goes out of scope, the receiver fails
2021-04-14 10:07:21 -07:00
let ( result_sender , result_receiver ) = unbounded ( ) ;
2021-03-29 22:51:35 -07:00
let res =
self . record_sender
. send ( Record ::new ( mixin , transactions , bank_slot , result_sender ) ) ;
2021-03-23 07:10:04 -07:00
if res . is_err ( ) {
// If the channel is dropped, then the validator is shutting down so return that we are hitting
// the max tick height to stop transaction processing and flush any transactions in the pipeline.
return Err ( PohRecorderError ::MaxHeightReached ) ;
}
2021-03-25 19:31:07 -07:00
// Besides validator exit, this timeout should primarily be seen to affect test execution environments where the various pieces can be shutdown abruptly
2021-04-14 10:07:21 -07:00
let mut is_exited = false ;
loop {
let res = result_receiver . recv_timeout ( Duration ::from_millis ( 1000 ) ) ;
match res {
Err ( RecvTimeoutError ::Timeout ) = > {
if is_exited {
return Err ( PohRecorderError ::MaxHeightReached ) ;
} else {
// A result may have come in between when we timed out checking this
// bool, so check the channel again, even if is_exited == true
is_exited = self . is_exited . load ( Ordering ::SeqCst ) ;
}
}
Err ( RecvTimeoutError ::Disconnected ) = > {
return Err ( PohRecorderError ::MaxHeightReached ) ;
}
Ok ( result ) = > {
return result ;
}
}
2021-03-23 07:10:04 -07:00
}
}
}
2021-09-17 16:55:53 -07:00
pub enum PohRecorderBank {
WorkingBank ( BankStart ) ,
LastResetBank ( Arc < Bank > ) ,
}
impl PohRecorderBank {
pub fn bank ( & self ) -> & Arc < Bank > {
match self {
PohRecorderBank ::WorkingBank ( bank_start ) = > & bank_start . working_bank ,
PohRecorderBank ::LastResetBank ( last_reset_bank ) = > last_reset_bank ,
}
}
pub fn working_bank_start ( & self ) -> Option < & BankStart > {
match self {
PohRecorderBank ::WorkingBank ( bank_start ) = > Some ( bank_start ) ,
PohRecorderBank ::LastResetBank ( _last_reset_bank ) = > None ,
}
}
}
2019-02-19 16:17:36 -08:00
#[ derive(Clone) ]
pub struct WorkingBank {
pub bank : Arc < Bank > ,
2021-03-15 17:11:15 -07:00
pub start : Arc < Instant > ,
2019-02-19 16:17:36 -08:00
pub min_tick_height : u64 ,
pub max_tick_height : u64 ,
2022-06-23 12:37:38 -07:00
pub transaction_index : Option < usize > ,
2018-10-18 22:57:48 -07:00
}
2022-05-22 18:00:42 -07:00
#[ derive(Debug, PartialEq, Eq) ]
2022-01-22 18:28:50 -08:00
pub enum PohLeaderStatus {
NotReached ,
Reached { poh_slot : Slot , parent_slot : Slot } ,
}
2018-09-26 05:52:13 -07:00
pub struct PohRecorder {
2019-05-18 14:01:36 -07:00
pub poh : Arc < Mutex < Poh > > ,
tick_height : u64 ,
2022-01-11 02:44:46 -08:00
clear_bank_signal : Option < Sender < bool > > ,
2021-09-13 16:55:35 -07:00
start_bank : Arc < Bank > , // parent slot
2019-10-16 12:53:11 -07:00
start_tick_height : u64 , // first tick_height this recorder will observe
tick_cache : Vec < ( Entry , u64 ) > , // cache of entry and its tick_height
2019-02-24 08:59:49 -08:00
working_bank : Option < WorkingBank > ,
2019-09-18 12:16:22 -07:00
sender : Sender < WorkingBankEntry > ,
2022-03-30 07:04:49 -07:00
poh_timing_point_sender : Option < PohTimingSender > ,
2022-01-22 18:28:50 -08:00
leader_first_tick_height_including_grace_ticks : Option < u64 > ,
2019-10-16 12:53:11 -07:00
leader_last_tick_height : u64 , // zero if none
2019-07-17 14:10:15 -07:00
grace_ticks : u64 ,
2019-03-20 13:49:46 -07:00
id : Pubkey ,
2020-01-13 13:13:52 -08:00
blockstore : Arc < Blockstore > ,
2019-04-19 02:39:44 -07:00
leader_schedule_cache : Arc < LeaderScheduleCache > ,
2019-05-18 14:01:36 -07:00
poh_config : Arc < PohConfig > ,
2019-05-06 11:23:06 -07:00
ticks_per_slot : u64 ,
2021-03-26 11:54:16 -07:00
target_ns_per_tick : u64 ,
2021-03-05 16:01:21 -08:00
record_lock_contention_us : u64 ,
2021-03-19 07:48:55 -07:00
flush_cache_no_tick_us : u64 ,
flush_cache_tick_us : u64 ,
2022-01-26 16:44:41 -08:00
send_entry_us : u64 ,
2021-03-05 16:01:21 -08:00
tick_lock_contention_us : u64 ,
2021-03-26 11:54:16 -07:00
total_sleep_us : u64 ,
2021-03-05 16:01:21 -08:00
record_us : u64 ,
2022-01-26 16:44:41 -08:00
report_metrics_us : u64 ,
2021-03-22 13:35:06 -07:00
ticks_from_record : u64 ,
2021-03-17 08:38:26 -07:00
last_metric : Instant ,
2022-01-11 02:44:46 -08:00
record_sender : Sender < Record > ,
2021-04-14 10:07:21 -07:00
pub is_exited : Arc < AtomicBool > ,
2018-09-26 05:52:13 -07:00
}
impl PohRecorder {
2019-04-09 17:53:04 -07:00
fn clear_bank ( & mut self ) {
2019-03-20 14:50:02 -07:00
if let Some ( working_bank ) = self . working_bank . take ( ) {
let bank = working_bank . bank ;
2019-04-19 02:39:44 -07:00
let next_leader_slot = self . leader_schedule_cache . next_leader_slot (
2019-03-29 20:00:36 -07:00
& self . id ,
bank . slot ( ) ,
& bank ,
2020-01-13 13:13:52 -08:00
Some ( & self . blockstore ) ,
2020-02-26 13:35:50 -08:00
GRACE_TICKS_FACTOR * MAX_GRACE_SLOTS ,
2019-03-29 20:00:36 -07:00
) ;
2019-07-09 15:36:30 -07:00
assert_eq! ( self . ticks_per_slot , bank . ticks_per_slot ( ) ) ;
2022-01-22 18:28:50 -08:00
let (
leader_first_tick_height_including_grace_ticks ,
leader_last_tick_height ,
grace_ticks ,
) = Self ::compute_leader_slot_tick_heights ( next_leader_slot , self . ticks_per_slot ) ;
2019-07-26 11:33:51 -07:00
self . grace_ticks = grace_ticks ;
2022-01-22 18:28:50 -08:00
self . leader_first_tick_height_including_grace_ticks =
leader_first_tick_height_including_grace_ticks ;
2019-10-16 12:53:11 -07:00
self . leader_last_tick_height = leader_last_tick_height ;
2021-03-15 17:11:15 -07:00
datapoint_info! (
" leader-slot-start-to-cleared-elapsed-ms " ,
( " slot " , bank . slot ( ) , i64 ) ,
( " elapsed " , working_bank . start . elapsed ( ) . as_millis ( ) , i64 ) ,
) ;
2019-03-20 13:49:46 -07:00
}
2021-03-15 17:11:15 -07:00
2019-03-11 13:58:23 -07:00
if let Some ( ref signal ) = self . clear_bank_signal {
2022-04-05 06:57:12 -07:00
match signal . try_send ( true ) {
Ok ( _ ) = > { }
Err ( TrySendError ::Full ( _ ) ) = > {
trace! ( " replay wake up signal channel is full. " )
}
Err ( TrySendError ::Disconnected ( _ ) ) = > {
trace! ( " replay wake up signal channel is disconnected. " )
}
}
2019-03-11 13:58:23 -07:00
}
2019-02-24 08:59:49 -08:00
}
2019-04-23 11:56:30 -07:00
pub fn would_be_leader ( & self , within_next_n_ticks : u64 ) -> bool {
2019-07-17 14:10:15 -07:00
self . has_bank ( )
2022-01-22 18:28:50 -08:00
| | self . leader_first_tick_height_including_grace_ticks . map_or (
false ,
| leader_first_tick_height_including_grace_ticks | {
let ideal_leader_tick_height = leader_first_tick_height_including_grace_ticks
. saturating_sub ( self . grace_ticks ) ;
2019-10-16 12:53:11 -07:00
self . tick_height + within_next_n_ticks > = ideal_leader_tick_height
& & self . tick_height < = self . leader_last_tick_height
2022-01-22 18:28:50 -08:00
} ,
)
}
// Return the slot for a given tick height
fn slot_for_tick_height ( & self , tick_height : u64 ) -> Slot {
// We need to subtract by one here because, assuming ticks per slot is 64,
// tick heights [1..64] correspond to slot 0. The last tick height of a slot
// is always a multiple of 64.
tick_height . saturating_sub ( 1 ) / self . ticks_per_slot
2019-05-01 11:37:29 -07:00
}
2019-10-19 20:28:33 -07:00
pub fn leader_after_n_slots ( & self , slots : u64 ) -> Option < Pubkey > {
2022-01-22 18:28:50 -08:00
let current_slot = self . slot_for_tick_height ( self . tick_height ) ;
2019-06-24 15:56:50 -07:00
self . leader_schedule_cache
2019-10-16 12:53:11 -07:00
. slot_leader_at ( current_slot + slots , None )
2019-06-24 15:56:50 -07:00
}
2019-07-18 14:54:27 -07:00
2019-07-17 14:10:15 -07:00
pub fn next_slot_leader ( & self ) -> Option < Pubkey > {
2019-10-19 20:28:33 -07:00
self . leader_after_n_slots ( 1 )
2019-03-12 17:42:53 -07:00
}
2019-03-03 16:44:06 -08:00
pub fn bank ( & self ) -> Option < Arc < Bank > > {
2021-03-15 17:11:15 -07:00
self . working_bank . as_ref ( ) . map ( | w | w . bank . clone ( ) )
}
pub fn bank_start ( & self ) -> Option < BankStart > {
2021-09-17 16:55:53 -07:00
self . working_bank . as_ref ( ) . map ( | w | BankStart {
working_bank : w . bank . clone ( ) ,
bank_creation_time : w . start . clone ( ) ,
} )
2019-03-03 16:44:06 -08:00
}
2019-03-12 17:42:53 -07:00
2022-03-30 07:04:49 -07:00
pub fn working_bank_end_slot ( & self ) -> Option < Slot > {
self . working_bank . as_ref ( ) . and_then ( | w | {
if w . max_tick_height = = self . tick_height {
Some ( w . bank . slot ( ) )
} else {
None
}
} )
}
pub fn working_slot ( & self ) -> Option < Slot > {
self . working_bank . as_ref ( ) . map ( | w | w . bank . slot ( ) )
}
2019-07-09 15:36:30 -07:00
pub fn has_bank ( & self ) -> bool {
self . working_bank . is_some ( )
}
2019-03-04 07:08:22 -08:00
pub fn tick_height ( & self ) -> u64 {
2019-05-18 14:01:36 -07:00
self . tick_height
2019-03-04 07:08:22 -08:00
}
2019-03-12 17:42:53 -07:00
2019-07-09 15:36:30 -07:00
pub fn ticks_per_slot ( & self ) -> u64 {
self . ticks_per_slot
}
2019-03-18 13:24:07 -07:00
2021-03-23 07:10:04 -07:00
pub fn recorder ( & self ) -> TransactionRecorder {
2021-04-14 10:07:21 -07:00
TransactionRecorder ::new ( self . record_sender . clone ( ) , self . is_exited . clone ( ) )
2021-03-23 07:10:04 -07:00
}
2020-08-19 01:16:24 -07:00
fn is_same_fork_as_previous_leader ( & self , slot : Slot ) -> bool {
( slot . saturating_sub ( NUM_CONSECUTIVE_LEADER_SLOTS ) .. slot ) . any ( | slot | {
// Check if the last slot Poh reset to was any of the
// 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
2021-09-13 16:55:35 -07:00
slot = = self . start_slot ( )
2020-01-13 15:55:41 -08:00
} )
}
2020-02-29 00:05:35 -08:00
fn prev_slot_was_mine ( & self , current_slot : Slot ) -> bool {
if let Some ( leader_id ) = self
. leader_schedule_cache
. slot_leader_at ( current_slot . saturating_sub ( 1 ) , None )
{
leader_id = = self . id
} else {
false
}
}
2022-01-22 18:28:50 -08:00
fn reached_leader_tick ( & self , leader_first_tick_height_including_grace_ticks : u64 ) -> bool {
let target_tick_height = leader_first_tick_height_including_grace_ticks . saturating_sub ( 1 ) ;
2020-01-13 15:55:41 -08:00
let ideal_target_tick_height = target_tick_height . saturating_sub ( self . grace_ticks ) ;
2022-01-22 18:28:50 -08:00
let next_tick_height = self . tick_height . saturating_add ( 1 ) ;
let next_slot = self . slot_for_tick_height ( next_tick_height ) ;
2020-08-19 01:16:24 -07:00
// We've approached target_tick_height OR poh was reset to run immediately
2020-01-13 15:55:41 -08:00
// Or, previous leader didn't transmit in any of its leader slots, so ignore grace ticks
self . tick_height > = target_tick_height
2022-01-22 18:28:50 -08:00
| | self . start_tick_height + self . grace_ticks
= = leader_first_tick_height_including_grace_ticks
2020-01-13 15:55:41 -08:00
| | ( self . tick_height > = ideal_target_tick_height
2022-01-22 18:28:50 -08:00
& & ( self . prev_slot_was_mine ( next_slot )
| | ! self . is_same_fork_as_previous_leader ( next_slot ) ) )
2020-01-13 15:55:41 -08:00
}
2021-09-13 16:55:35 -07:00
pub fn start_slot ( & self ) -> Slot {
self . start_bank . slot ( )
2021-03-24 23:41:52 -07:00
}
2022-01-22 18:28:50 -08:00
/// Returns if the leader slot has been reached along with the current poh
/// slot and the parent slot (could be a few slots ago if any previous
/// leaders needed to be skipped).
pub fn reached_leader_slot ( & self ) -> PohLeaderStatus {
2019-07-09 15:36:30 -07:00
trace! (
2022-01-22 18:28:50 -08:00
" tick_height {}, start_tick_height {}, leader_first_tick_height_including_grace_ticks {:?}, grace_ticks {}, has_bank {} " ,
2019-07-09 15:36:30 -07:00
self . tick_height ,
2019-10-16 12:53:11 -07:00
self . start_tick_height ,
2022-01-22 18:28:50 -08:00
self . leader_first_tick_height_including_grace_ticks ,
2019-07-17 14:10:15 -07:00
self . grace_ticks ,
self . has_bank ( )
2019-07-09 15:36:30 -07:00
) ;
2019-10-16 12:53:11 -07:00
let next_tick_height = self . tick_height + 1 ;
2022-01-22 18:28:50 -08:00
let next_poh_slot = self . slot_for_tick_height ( next_tick_height ) ;
if let Some ( leader_first_tick_height_including_grace_ticks ) =
self . leader_first_tick_height_including_grace_ticks
{
if self . reached_leader_tick ( leader_first_tick_height_including_grace_ticks ) {
2019-10-16 12:53:11 -07:00
assert! ( next_tick_height > = self . start_tick_height ) ;
2022-01-22 18:28:50 -08:00
let poh_slot = next_poh_slot ;
let parent_slot = self . start_slot ( ) ;
return PohLeaderStatus ::Reached {
poh_slot ,
parent_slot ,
} ;
2019-07-09 15:36:30 -07:00
}
}
2022-01-22 18:28:50 -08:00
PohLeaderStatus ::NotReached
2019-03-15 13:22:16 -07:00
}
2022-01-22 18:28:50 -08:00
// returns (leader_first_tick_height_including_grace_ticks, leader_last_tick_height, grace_ticks) given the next
2019-10-16 12:53:11 -07:00
// slot this recorder will lead
fn compute_leader_slot_tick_heights (
2019-07-26 11:33:51 -07:00
next_leader_slot : Option < ( Slot , Slot ) > ,
2019-03-20 15:14:21 -07:00
ticks_per_slot : u64 ,
2019-07-26 11:33:51 -07:00
) -> ( Option < u64 > , u64 , u64 ) {
2019-03-20 15:14:21 -07:00
next_leader_slot
2019-10-16 12:53:11 -07:00
. map ( | ( first_slot , last_slot ) | {
let leader_first_tick_height = first_slot * ticks_per_slot + 1 ;
let last_tick_height = ( last_slot + 1 ) * ticks_per_slot ;
let num_slots = last_slot - first_slot + 1 ;
2019-07-26 11:33:51 -07:00
let grace_ticks = cmp ::min (
2019-11-19 17:55:42 -08:00
ticks_per_slot * MAX_GRACE_SLOTS ,
2019-10-16 12:53:11 -07:00
ticks_per_slot * num_slots / GRACE_TICKS_FACTOR ,
2019-07-26 11:33:51 -07:00
) ;
2022-01-22 18:28:50 -08:00
let leader_first_tick_height_including_grace_ticks =
leader_first_tick_height + grace_ticks ;
2019-10-16 12:53:11 -07:00
(
2022-01-22 18:28:50 -08:00
Some ( leader_first_tick_height_including_grace_ticks ) ,
2019-10-16 12:53:11 -07:00
last_tick_height ,
grace_ticks ,
)
2019-03-20 15:14:21 -07:00
} )
2019-07-26 11:33:51 -07:00
. unwrap_or ( (
None ,
0 ,
cmp ::min (
2019-11-19 17:55:42 -08:00
ticks_per_slot * MAX_GRACE_SLOTS ,
2019-07-26 11:33:51 -07:00
ticks_per_slot * NUM_CONSECUTIVE_LEADER_SLOTS / GRACE_TICKS_FACTOR ,
) ,
) )
2019-03-20 15:14:21 -07:00
}
2019-02-24 08:59:49 -08:00
// synchronize PoH with a bank
2021-09-13 16:55:35 -07:00
pub fn reset ( & mut self , reset_bank : Arc < Bank > , next_leader_slot : Option < ( Slot , Slot ) > ) {
2019-03-05 17:56:51 -08:00
self . clear_bank ( ) ;
2021-09-13 16:55:35 -07:00
let blockhash = reset_bank . last_blockhash ( ) ;
2021-03-17 08:38:26 -07:00
let poh_hash = {
2019-05-18 14:01:36 -07:00
let mut poh = self . poh . lock ( ) . unwrap ( ) ;
poh . reset ( blockhash , self . poh_config . hashes_per_tick ) ;
2021-03-17 08:38:26 -07:00
poh . hash
} ;
info! (
" reset poh from: {},{},{} to: {},{} " ,
2021-09-13 16:55:35 -07:00
poh_hash ,
self . tick_height ,
self . start_slot ( ) ,
blockhash ,
reset_bank . slot ( )
2021-03-17 08:38:26 -07:00
) ;
2019-05-18 14:01:36 -07:00
2022-07-11 04:33:15 -07:00
self . tick_cache = vec! [ ] ;
2021-09-13 16:55:35 -07:00
self . start_bank = reset_bank ;
self . tick_height = ( self . start_slot ( ) + 1 ) * self . ticks_per_slot ;
2019-10-16 12:53:11 -07:00
self . start_tick_height = self . tick_height + 1 ;
2019-07-17 14:10:15 -07:00
2022-03-30 07:04:49 -07:00
if let Some ( ref sender ) = self . poh_timing_point_sender {
// start_slot() is the parent slot. current slot is start_slot() + 1.
send_poh_timing_point (
sender ,
SlotPohTimingInfo ::new_slot_start_poh_time_point (
self . start_slot ( ) + 1 ,
None ,
solana_sdk ::timing ::timestamp ( ) ,
) ,
) ;
}
2022-01-22 18:28:50 -08:00
let ( leader_first_tick_height_including_grace_ticks , leader_last_tick_height , grace_ticks ) =
2019-10-16 12:53:11 -07:00
Self ::compute_leader_slot_tick_heights ( next_leader_slot , self . ticks_per_slot ) ;
2019-07-26 11:33:51 -07:00
self . grace_ticks = grace_ticks ;
2022-01-22 18:28:50 -08:00
self . leader_first_tick_height_including_grace_ticks =
leader_first_tick_height_including_grace_ticks ;
2019-10-16 12:53:11 -07:00
self . leader_last_tick_height = leader_last_tick_height ;
2019-02-24 08:59:49 -08:00
}
2022-06-23 12:37:38 -07:00
pub fn set_bank ( & mut self , bank : & Arc < Bank > , track_transaction_indexes : bool ) {
2019-03-03 16:44:06 -08:00
let working_bank = WorkingBank {
bank : bank . clone ( ) ,
2021-03-15 17:11:15 -07:00
start : Arc ::new ( Instant ::now ( ) ) ,
2019-03-03 16:44:06 -08:00
min_tick_height : bank . tick_height ( ) ,
2019-07-09 15:36:30 -07:00
max_tick_height : bank . max_tick_height ( ) ,
2022-08-22 18:01:03 -07:00
transaction_index : track_transaction_indexes . then_some ( 0 ) ,
2019-03-03 16:44:06 -08:00
} ;
2021-09-13 16:55:35 -07:00
trace! ( " new working bank " ) ;
assert_eq! ( working_bank . bank . ticks_per_slot ( ) , self . ticks_per_slot ( ) ) ;
self . working_bank = Some ( working_bank ) ;
2022-03-30 07:04:49 -07:00
// send poh slot start timing point
if let Some ( ref sender ) = self . poh_timing_point_sender {
if let Some ( slot ) = self . working_slot ( ) {
send_poh_timing_point (
sender ,
SlotPohTimingInfo ::new_slot_start_poh_time_point (
slot ,
None ,
solana_sdk ::timing ::timestamp ( ) ,
) ,
) ;
}
}
2021-09-13 16:55:35 -07:00
// 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 ) ;
2019-03-03 16:44:06 -08:00
}
2019-02-24 08:59:49 -08:00
// Flush cache will delay flushing the cache for a bank until it past the WorkingBank::min_tick_height
// On a record flush will flush the cache at the WorkingBank::min_tick_height, since a record
// occurs after the min_tick_height was generated
fn flush_cache ( & mut self , tick : bool ) -> Result < ( ) > {
// check_tick_height is called before flush cache, so it cannot overrun the bank
// so a bank that is so late that it's slot fully generated before it starts recording
// will fail instead of broadcasting any ticks
let working_bank = self
. working_bank
. as_ref ( )
2020-01-02 19:50:43 -08:00
. ok_or ( PohRecorderError ::MaxHeightReached ) ? ;
2019-05-18 14:01:36 -07:00
if self . tick_height < working_bank . min_tick_height {
2020-01-02 19:50:43 -08:00
return Err ( PohRecorderError ::MinHeightNotReached ) ;
2019-02-24 08:59:49 -08:00
}
2019-05-18 14:01:36 -07:00
if tick & & self . tick_height = = working_bank . min_tick_height {
2020-01-02 19:50:43 -08:00
return Err ( PohRecorderError ::MinHeightNotReached ) ;
2019-02-24 08:59:49 -08:00
}
2019-05-18 14:01:36 -07:00
let entry_count = self
2019-02-24 08:59:49 -08:00
. tick_cache
. iter ( )
. take_while ( | x | x . 1 < = working_bank . max_tick_height )
. count ( ) ;
2019-09-18 12:16:22 -07:00
let mut send_result : std ::result ::Result < ( ) , SendError < WorkingBankEntry > > = Ok ( ( ) ) ;
if entry_count > 0 {
2019-07-17 14:10:15 -07:00
trace! (
2019-03-04 16:40:28 -08:00
" flush_cache: bank_slot: {} tick_height: {} max: {} sending: {} " ,
2019-02-26 21:57:45 -08:00
working_bank . bank . slot ( ) ,
2019-02-24 08:59:49 -08:00
working_bank . bank . tick_height ( ) ,
working_bank . max_tick_height ,
2019-05-18 14:01:36 -07:00
entry_count ,
2019-02-24 08:59:49 -08:00
) ;
2019-09-18 12:16:22 -07:00
for tick in & self . tick_cache [ .. entry_count ] {
working_bank . bank . register_tick ( & tick . 0. hash ) ;
send_result = self . sender . send ( ( working_bank . bank . clone ( ) , tick . clone ( ) ) ) ;
if send_result . is_err ( ) {
break ;
}
2019-02-19 16:17:36 -08:00
}
2019-09-18 12:16:22 -07:00
}
2019-05-18 14:01:36 -07:00
if self . tick_height > = working_bank . max_tick_height {
2019-03-03 16:44:06 -08:00
info! (
2019-07-09 15:36:30 -07:00
" poh_record: max_tick_height {} reached, clearing working_bank {} " ,
working_bank . max_tick_height ,
2019-03-03 16:44:06 -08:00
working_bank . bank . slot ( )
) ;
2021-09-13 16:55:35 -07:00
self . start_bank = working_bank . bank . clone ( ) ;
let working_slot = self . start_slot ( ) ;
2019-10-16 12:53:11 -07:00
self . start_tick_height = working_slot * self . ticks_per_slot + 1 ;
2019-03-11 13:58:23 -07:00
self . clear_bank ( ) ;
2019-02-19 16:17:36 -08:00
}
2019-05-18 14:01:36 -07:00
if send_result . is_err ( ) {
info! ( " WorkingBank::sender disconnected {:?} " , send_result ) ;
// revert the cache, but clear the working bank
2019-03-11 13:58:23 -07:00
self . clear_bank ( ) ;
2019-02-24 08:59:49 -08:00
} else {
2019-05-18 14:01:36 -07:00
// commit the flush
let _ = self . tick_cache . drain ( .. entry_count ) ;
2019-02-24 08:59:49 -08:00
}
2018-12-12 18:52:11 -08:00
Ok ( ( ) )
2018-09-26 05:52:13 -07:00
}
2022-03-30 07:04:49 -07:00
fn report_poh_timing_point_by_tick ( & self ) {
match self . tick_height % self . ticks_per_slot {
// reaching the end of the slot
0 = > {
if let Some ( ref sender ) = self . poh_timing_point_sender {
send_poh_timing_point (
sender ,
SlotPohTimingInfo ::new_slot_end_poh_time_point (
self . slot_for_tick_height ( self . tick_height ) ,
None ,
solana_sdk ::timing ::timestamp ( ) ,
) ,
) ;
}
}
// beginning of a slot
1 = > {
if let Some ( ref sender ) = self . poh_timing_point_sender {
send_poh_timing_point (
sender ,
SlotPohTimingInfo ::new_slot_start_poh_time_point (
self . slot_for_tick_height ( self . tick_height ) ,
None ,
solana_sdk ::timing ::timestamp ( ) ,
) ,
) ;
}
}
_ = > { }
}
}
fn report_poh_timing_point_by_working_bank ( & self , slot : Slot ) {
if let Some ( ref sender ) = self . poh_timing_point_sender {
send_poh_timing_point (
sender ,
SlotPohTimingInfo ::new_slot_end_poh_time_point (
slot ,
None ,
solana_sdk ::timing ::timestamp ( ) ,
) ,
) ;
}
}
fn report_poh_timing_point ( & self ) {
// send poh slot end timing point
if let Some ( slot ) = self . working_bank_end_slot ( ) {
// bank producer
self . report_poh_timing_point_by_working_bank ( slot )
} else {
// validator
self . report_poh_timing_point_by_tick ( )
}
}
2019-02-24 08:59:49 -08:00
pub fn tick ( & mut self ) {
2022-06-06 18:21:05 -07:00
let ( ( poh_entry , target_time ) , tick_lock_contention_time ) = measure! (
{
2022-01-26 16:44:41 -08:00
let mut poh_l = self . poh . lock ( ) . unwrap ( ) ;
let poh_entry = poh_l . tick ( ) ;
let target_time = if poh_entry . is_some ( ) {
Some ( poh_l . target_poh_time ( self . target_ns_per_tick ) )
} else {
None
} ;
( poh_entry , target_time )
} ,
2022-01-27 17:35:13 -08:00
" tick_lock_contention " ,
2022-01-26 16:44:41 -08:00
) ;
self . tick_lock_contention_us + = tick_lock_contention_time . as_us ( ) ;
2019-05-18 14:01:36 -07:00
if let Some ( poh_entry ) = poh_entry {
self . tick_height + = 1 ;
2019-10-16 12:53:11 -07:00
trace! ( " tick_height {} " , self . tick_height ) ;
2022-03-30 07:04:49 -07:00
self . report_poh_timing_point ( ) ;
2019-05-03 16:41:19 -07:00
2022-01-22 18:28:50 -08:00
if self
. leader_first_tick_height_including_grace_ticks
. is_none ( )
{
2019-05-18 14:01:36 -07:00
return ;
}
2019-03-15 13:22:16 -07:00
2022-01-26 16:44:41 -08:00
self . tick_cache . push ( (
Entry {
num_hashes : poh_entry . num_hashes ,
hash : poh_entry . hash ,
transactions : vec ! [ ] ,
} ,
self . tick_height ,
) ) ;
let ( _flush_res , flush_cache_and_tick_time ) =
2022-06-06 18:21:05 -07:00
measure! ( self . flush_cache ( true ) , " flush_cache_and_tick " ) ;
2022-01-26 16:44:41 -08:00
self . flush_cache_tick_us + = flush_cache_and_tick_time . as_us ( ) ;
2022-06-06 18:21:05 -07:00
let sleep_time = measure! (
{
2022-01-26 16:44:41 -08:00
let target_time = target_time . unwrap ( ) ;
// sleep is not accurate enough to get a predictable time.
// Kernel can not schedule the thread for a while.
while Instant ::now ( ) < target_time {
// TODO: a caller could possibly desire to reset or record while we're spinning here
std ::hint ::spin_loop ( ) ;
}
} ,
2022-01-27 17:35:13 -08:00
" poh_sleep " ,
2022-01-26 16:44:41 -08:00
)
. 1 ;
self . total_sleep_us + = sleep_time . as_us ( ) ;
2019-05-18 14:01:36 -07:00
}
2021-03-05 16:01:21 -08:00
}
fn report_metrics ( & mut self , bank_slot : Slot ) {
2021-03-17 08:38:26 -07:00
if self . last_metric . elapsed ( ) . as_millis ( ) > 1000 {
datapoint_info! (
" poh_recorder " ,
( " slot " , bank_slot , i64 ) ,
( " tick_lock_contention " , self . tick_lock_contention_us , i64 ) ,
( " record_us " , self . record_us , i64 ) ,
2021-03-19 07:48:55 -07:00
( " flush_cache_no_tick_us " , self . flush_cache_no_tick_us , i64 ) ,
( " flush_cache_tick_us " , self . flush_cache_tick_us , i64 ) ,
2022-01-26 16:44:41 -08:00
( " send_entry_us " , self . send_entry_us , i64 ) ,
2021-03-22 13:35:06 -07:00
( " ticks_from_record " , self . ticks_from_record , i64 ) ,
2021-03-26 11:54:16 -07:00
( " total_sleep_us " , self . total_sleep_us , i64 ) ,
2021-03-17 08:38:26 -07:00
(
2022-01-26 16:44:41 -08:00
" record_lock_contention_us " ,
2021-03-17 08:38:26 -07:00
self . record_lock_contention_us ,
i64
) ,
2022-01-26 16:44:41 -08:00
( " report_metrics_us " , self . report_metrics_us , i64 ) ,
2021-03-17 08:38:26 -07:00
) ;
2021-03-05 16:01:21 -08:00
2021-03-17 08:38:26 -07:00
self . tick_lock_contention_us = 0 ;
self . record_us = 0 ;
2021-03-26 11:54:16 -07:00
self . total_sleep_us = 0 ;
2021-03-17 08:38:26 -07:00
self . record_lock_contention_us = 0 ;
2021-03-19 07:48:55 -07:00
self . flush_cache_no_tick_us = 0 ;
self . flush_cache_tick_us = 0 ;
2022-01-26 16:44:41 -08:00
self . send_entry_us = 0 ;
2021-03-22 13:35:06 -07:00
self . ticks_from_record = 0 ;
2022-01-26 16:44:41 -08:00
self . report_metrics_us = 0 ;
2021-03-17 08:38:26 -07:00
self . last_metric = Instant ::now ( ) ;
}
2018-09-26 05:52:13 -07:00
}
2022-06-23 12:37:38 -07:00
// Returns the index of `transactions.first()` in the slot, if being tracked by WorkingBank
2019-05-18 14:01:36 -07:00
pub fn record (
& mut self ,
2019-07-09 15:36:30 -07:00
bank_slot : Slot ,
2019-05-18 14:01:36 -07:00
mixin : Hash ,
2021-08-17 15:17:56 -07:00
transactions : Vec < VersionedTransaction > ,
2022-06-23 12:37:38 -07:00
) -> Result < Option < usize > > {
2019-05-18 14:01:36 -07:00
// Entries without transactions are used to track real-time passing in the ledger and
// cannot be generated by `record()`
assert! ( ! transactions . is_empty ( ) , " No transactions provided " ) ;
2022-01-26 16:44:41 -08:00
2022-06-06 18:21:05 -07:00
let ( ( ) , report_metrics_time ) = measure! ( self . report_metrics ( bank_slot ) , " report_metrics " ) ;
2022-01-26 16:44:41 -08:00
self . report_metrics_us + = report_metrics_time . as_us ( ) ;
2019-05-18 14:01:36 -07:00
loop {
2022-01-26 16:44:41 -08:00
let ( flush_cache_res , flush_cache_time ) =
2022-06-06 18:21:05 -07:00
measure! ( self . flush_cache ( false ) , " flush_cache " ) ;
2022-01-26 16:44:41 -08:00
self . flush_cache_no_tick_us + = flush_cache_time . as_us ( ) ;
flush_cache_res ? ;
2019-05-18 14:01:36 -07:00
let working_bank = self
. working_bank
2022-06-23 12:37:38 -07:00
. as_mut ( )
2020-01-02 19:50:43 -08:00
. ok_or ( PohRecorderError ::MaxHeightReached ) ? ;
2019-05-18 14:01:36 -07:00
if bank_slot ! = working_bank . bank . slot ( ) {
2020-01-02 19:50:43 -08:00
return Err ( PohRecorderError ::MaxHeightReached ) ;
2019-05-18 14:01:36 -07:00
}
2022-06-06 18:21:05 -07:00
let ( mut poh_lock , poh_lock_time ) = measure! ( self . poh . lock ( ) . unwrap ( ) , " poh_lock " ) ;
2022-01-26 16:44:41 -08:00
self . record_lock_contention_us + = poh_lock_time . as_us ( ) ;
let ( record_mixin_res , record_mixin_time ) =
2022-06-06 18:21:05 -07:00
measure! ( poh_lock . record ( mixin ) , " record_mixin " ) ;
2022-01-26 16:44:41 -08:00
self . record_us + = record_mixin_time . as_us ( ) ;
drop ( poh_lock ) ;
if let Some ( poh_entry ) = record_mixin_res {
2022-06-23 12:37:38 -07:00
let num_transactions = transactions . len ( ) ;
2022-06-06 18:21:05 -07:00
let ( send_entry_res , send_entry_time ) = measure! (
{
2022-01-26 16:44:41 -08:00
let entry = Entry {
num_hashes : poh_entry . num_hashes ,
hash : poh_entry . hash ,
transactions ,
} ;
let bank_clone = working_bank . bank . clone ( ) ;
self . sender . send ( ( bank_clone , ( entry , self . tick_height ) ) )
} ,
2022-01-27 17:35:13 -08:00
" send_poh_entry " ,
2022-01-26 16:44:41 -08:00
) ;
self . send_entry_us + = send_entry_time . as_us ( ) ;
2022-06-23 12:37:38 -07:00
send_entry_res ? ;
let starting_transaction_index =
working_bank . transaction_index . map ( | transaction_index | {
let next_starting_transaction_index =
transaction_index . saturating_add ( num_transactions ) ;
working_bank . transaction_index = Some ( next_starting_transaction_index ) ;
transaction_index
} ) ;
return Ok ( starting_transaction_index ) ;
2019-05-18 14:01:36 -07:00
}
2022-01-26 16:44:41 -08:00
2019-05-18 14:01:36 -07:00
// record() might fail if the next PoH hash needs to be a tick. But that's ok, tick()
// and re-record()
2021-03-22 13:35:06 -07:00
self . ticks_from_record + = 1 ;
2019-05-18 14:01:36 -07:00
self . tick ( ) ;
}
2018-10-18 22:57:48 -07:00
}
2019-05-18 14:01:36 -07:00
#[ allow(clippy::too_many_arguments) ]
2019-04-09 17:53:04 -07:00
pub fn new_with_clear_signal (
2019-03-12 17:42:53 -07:00
tick_height : u64 ,
last_entry_hash : Hash ,
2021-09-13 16:55:35 -07:00
start_bank : Arc < Bank > ,
2019-07-26 11:33:51 -07:00
next_leader_slot : Option < ( Slot , Slot ) > ,
2019-03-15 13:22:16 -07:00
ticks_per_slot : u64 ,
2019-03-20 14:23:58 -07:00
id : & Pubkey ,
2020-01-13 13:13:52 -08:00
blockstore : & Arc < Blockstore > ,
2022-01-11 02:44:46 -08:00
clear_bank_signal : Option < Sender < bool > > ,
2019-04-19 02:39:44 -07:00
leader_schedule_cache : & Arc < LeaderScheduleCache > ,
2019-05-18 14:01:36 -07:00
poh_config : & Arc < PohConfig > ,
2022-03-30 07:04:49 -07:00
poh_timing_point_sender : Option < PohTimingSender > ,
2021-04-14 10:07:21 -07:00
is_exited : Arc < AtomicBool > ,
2022-01-11 02:44:46 -08:00
) -> ( Self , Receiver < WorkingBankEntry > , Receiver < Record > ) {
2021-03-26 11:54:16 -07:00
let tick_number = 0 ;
let poh = Arc ::new ( Mutex ::new ( Poh ::new_with_slot_info (
2019-05-18 14:01:36 -07:00
last_entry_hash ,
poh_config . hashes_per_tick ,
2021-03-26 11:54:16 -07:00
ticks_per_slot ,
tick_number ,
2019-05-18 14:01:36 -07:00
) ) ) ;
2021-03-26 11:54:16 -07:00
let target_ns_per_tick = PohService ::target_ns_per_tick (
ticks_per_slot ,
poh_config . target_tick_duration . as_nanos ( ) as u64 ,
) ;
2022-01-11 02:44:46 -08:00
let ( sender , receiver ) = unbounded ( ) ;
2021-04-14 10:07:21 -07:00
let ( record_sender , record_receiver ) = unbounded ( ) ;
2022-01-22 18:28:50 -08:00
let ( leader_first_tick_height_including_grace_ticks , leader_last_tick_height , grace_ticks ) =
2019-10-16 12:53:11 -07:00
Self ::compute_leader_slot_tick_heights ( next_leader_slot , ticks_per_slot ) ;
2019-03-03 16:44:06 -08:00
(
2019-04-09 17:53:04 -07:00
Self {
2019-03-03 16:44:06 -08:00
poh ,
2019-05-18 14:01:36 -07:00
tick_height ,
2019-03-03 16:44:06 -08:00
tick_cache : vec ! [ ] ,
working_bank : None ,
sender ,
2022-03-30 07:04:49 -07:00
poh_timing_point_sender ,
2019-04-09 17:53:04 -07:00
clear_bank_signal ,
2021-09-13 16:55:35 -07:00
start_bank ,
2019-10-16 12:53:11 -07:00
start_tick_height : tick_height + 1 ,
2022-01-22 18:28:50 -08:00
leader_first_tick_height_including_grace_ticks ,
2019-10-16 12:53:11 -07:00
leader_last_tick_height ,
2019-07-17 14:10:15 -07:00
grace_ticks ,
2019-03-20 14:23:58 -07:00
id : * id ,
2020-01-13 13:13:52 -08:00
blockstore : blockstore . clone ( ) ,
2019-04-19 02:39:44 -07:00
leader_schedule_cache : leader_schedule_cache . clone ( ) ,
2019-05-06 11:23:06 -07:00
ticks_per_slot ,
2021-03-26 11:54:16 -07:00
target_ns_per_tick ,
2019-05-18 14:01:36 -07:00
poh_config : poh_config . clone ( ) ,
2021-03-05 16:01:21 -08:00
record_lock_contention_us : 0 ,
2021-03-19 07:48:55 -07:00
flush_cache_tick_us : 0 ,
flush_cache_no_tick_us : 0 ,
2022-01-26 16:44:41 -08:00
send_entry_us : 0 ,
2021-03-05 16:01:21 -08:00
tick_lock_contention_us : 0 ,
record_us : 0 ,
2022-01-26 16:44:41 -08:00
report_metrics_us : 0 ,
2021-03-26 11:54:16 -07:00
total_sleep_us : 0 ,
2021-03-22 13:35:06 -07:00
ticks_from_record : 0 ,
2021-03-17 08:38:26 -07:00
last_metric : Instant ::now ( ) ,
2021-03-23 07:10:04 -07:00
record_sender ,
2021-04-14 10:07:21 -07:00
is_exited ,
2019-03-03 16:44:06 -08:00
} ,
receiver ,
2021-03-23 07:10:04 -07:00
record_receiver ,
2019-03-03 16:44:06 -08:00
)
2018-10-18 22:57:48 -07:00
}
2019-04-09 17:53:04 -07:00
/// A recorder to synchronize PoH with the following data structures
/// * bank - the LastId's queue is updated on `tick` and `record` events
/// * sender - the Entry channel that outputs to the ledger
2021-04-14 10:07:21 -07:00
#[ allow(clippy::too_many_arguments) ]
2019-04-09 17:53:04 -07:00
pub fn new (
tick_height : u64 ,
last_entry_hash : Hash ,
2021-09-13 16:55:35 -07:00
start_bank : Arc < Bank > ,
2019-07-26 11:33:51 -07:00
next_leader_slot : Option < ( Slot , Slot ) > ,
2019-04-09 17:53:04 -07:00
ticks_per_slot : u64 ,
id : & Pubkey ,
2020-01-13 13:13:52 -08:00
blockstore : & Arc < Blockstore > ,
2019-04-19 02:39:44 -07:00
leader_schedule_cache : & Arc < LeaderScheduleCache > ,
2019-05-18 14:01:36 -07:00
poh_config : & Arc < PohConfig > ,
2021-04-14 10:07:21 -07:00
is_exited : Arc < AtomicBool > ,
2022-01-11 02:44:46 -08:00
) -> ( Self , Receiver < WorkingBankEntry > , Receiver < Record > ) {
2019-04-09 17:53:04 -07:00
Self ::new_with_clear_signal (
tick_height ,
last_entry_hash ,
2021-09-13 16:55:35 -07:00
start_bank ,
2019-07-09 15:36:30 -07:00
next_leader_slot ,
2019-04-09 17:53:04 -07:00
ticks_per_slot ,
id ,
2020-01-13 13:13:52 -08:00
blockstore ,
2019-04-09 17:53:04 -07:00
None ,
2019-04-19 02:39:44 -07:00
leader_schedule_cache ,
2019-05-18 14:01:36 -07:00
poh_config ,
2022-03-30 07:04:49 -07:00
None ,
2021-04-14 10:07:21 -07:00
is_exited ,
2019-02-24 08:59:49 -08:00
)
2018-10-18 22:57:48 -07:00
}
2021-02-12 03:27:37 -08:00
2021-09-17 16:55:53 -07:00
pub fn get_poh_recorder_bank ( & self ) -> PohRecorderBank {
let bank_start = self . bank_start ( ) ;
if let Some ( bank_start ) = bank_start {
PohRecorderBank ::WorkingBank ( bank_start )
} else {
PohRecorderBank ::LastResetBank ( self . start_bank . clone ( ) )
}
}
2021-03-15 17:11:15 -07:00
// Filters the return result of PohRecorder::bank_start(), returns the bank
// if it's still processing transactions
2021-09-17 16:55:53 -07:00
pub fn get_working_bank_if_not_expired < ' a , ' b > (
bank_start : & ' b Option < & ' a BankStart > ,
) -> Option < & ' a Arc < Bank > > {
bank_start
. as_ref ( )
. and_then ( | bank_start | bank_start . get_working_bank_if_not_expired ( ) )
2021-03-15 17:11:15 -07:00
}
2021-06-04 08:23:06 -07:00
// Used in tests
2021-02-12 03:27:37 -08:00
pub fn schedule_dummy_max_height_reached_failure ( & mut self ) {
2021-09-13 16:55:35 -07:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
self . reset ( bank , None ) ;
2021-02-12 03:27:37 -08:00
}
2018-09-26 05:52:13 -07:00
}
2021-06-04 08:23:06 -07:00
pub fn create_test_recorder (
bank : & Arc < Bank > ,
blockstore : & Arc < Blockstore > ,
poh_config : Option < PohConfig > ,
2022-02-07 18:28:28 -08:00
leader_schedule_cache : Option < Arc < LeaderScheduleCache > > ,
2021-06-04 08:23:06 -07:00
) -> (
Arc < AtomicBool > ,
2022-07-05 07:29:44 -07:00
Arc < RwLock < PohRecorder > > ,
2021-06-04 08:23:06 -07:00
PohService ,
Receiver < WorkingBankEntry > ,
) {
2022-02-07 18:28:28 -08:00
let leader_schedule_cache = match leader_schedule_cache {
Some ( provided_cache ) = > provided_cache ,
None = > Arc ::new ( LeaderScheduleCache ::new_from_bank ( bank ) ) ,
} ;
2021-06-04 08:23:06 -07:00
let exit = Arc ::new ( AtomicBool ::new ( false ) ) ;
let poh_config = Arc ::new ( poh_config . unwrap_or_default ( ) ) ;
let ( mut poh_recorder , entry_receiver , record_receiver ) = PohRecorder ::new (
bank . tick_height ( ) ,
bank . last_blockhash ( ) ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2021-06-04 08:23:06 -07:00
Some ( ( 4 , 4 ) ) ,
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
blockstore ,
2022-02-07 18:28:28 -08:00
& leader_schedule_cache ,
2021-06-04 08:23:06 -07:00
& poh_config ,
exit . clone ( ) ,
) ;
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( bank , false ) ;
2021-06-04 08:23:06 -07:00
2022-07-05 07:29:44 -07:00
let poh_recorder = Arc ::new ( RwLock ::new ( poh_recorder ) ) ;
2021-06-04 08:23:06 -07:00
let poh_service = PohService ::new (
poh_recorder . clone ( ) ,
& poh_config ,
& exit ,
bank . ticks_per_slot ( ) ,
crate ::poh_service ::DEFAULT_PINNED_CPU_CORE ,
crate ::poh_service ::DEFAULT_HASHES_PER_BATCH ,
record_receiver ,
) ;
( exit , poh_recorder , poh_service , entry_receiver )
}
2018-09-26 05:52:13 -07:00
#[ cfg(test) ]
mod tests {
2021-06-04 08:23:06 -07:00
use {
super ::* ,
bincode ::serialize ,
2022-01-11 02:44:46 -08:00
crossbeam_channel ::bounded ,
2021-09-13 16:55:35 -07:00
solana_ledger ::{ blockstore ::Blockstore , blockstore_meta ::SlotMeta , get_tmp_ledger_path } ,
2021-06-04 08:23:06 -07:00
solana_perf ::test_tx ::test_tx ,
solana_sdk ::{ clock ::DEFAULT_TICKS_PER_SLOT , hash ::hash } ,
} ;
2018-09-26 05:52:13 -07:00
#[ test ]
2019-02-24 08:59:49 -08:00
fn test_poh_recorder_no_zero_tick ( ) {
2019-03-01 08:44:55 -08:00
let prev_hash = Hash ::default ( ) ;
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-03-29 20:00:36 -07:00
2021-09-13 16:55:35 -07:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
DEFAULT_TICKS_PER_SLOT ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::default ( ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 1 ) ;
assert_eq! ( poh_recorder . tick_cache [ 0 ] . 1 , 1 ) ;
2019-05-18 14:01:36 -07:00
assert_eq! ( poh_recorder . tick_height , 1 ) ;
2019-03-29 20:00:36 -07:00
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-02-24 08:59:49 -08:00
}
#[ test ]
fn test_poh_recorder_tick_height_is_last_tick ( ) {
2019-03-01 08:44:55 -08:00
let prev_hash = Hash ::default ( ) ;
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-03-29 20:00:36 -07:00
2021-09-13 16:55:35 -07:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
DEFAULT_TICKS_PER_SLOT ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::default ( ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 2 ) ;
assert_eq! ( poh_recorder . tick_cache [ 1 ] . 1 , 2 ) ;
2019-05-18 14:01:36 -07:00
assert_eq! ( poh_recorder . tick_height , 2 ) ;
2019-03-29 20:00:36 -07:00
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-02-24 08:59:49 -08:00
}
#[ test ]
fn test_poh_recorder_reset_clears_cache ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2021-09-13 16:55:35 -07:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
let bank0 = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
Hash ::default ( ) ,
2021-09-13 16:55:35 -07:00
bank0 . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
DEFAULT_TICKS_PER_SLOT ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::default ( ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 1 ) ;
2021-09-13 16:55:35 -07:00
poh_recorder . reset ( bank0 , Some ( ( 4 , 4 ) ) ) ;
2019-03-29 20:00:36 -07:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-02-24 08:59:49 -08:00
}
#[ test ]
fn test_poh_recorder_clear ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-08-05 06:42:38 -07:00
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2019-03-29 20:00:36 -07:00
let prev_hash = bank . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-24 08:59:49 -08:00
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank , false ) ;
2019-03-29 20:00:36 -07:00
assert! ( poh_recorder . working_bank . is_some ( ) ) ;
poh_recorder . clear_bank ( ) ;
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-02-24 08:59:49 -08:00
}
#[ test ]
fn test_poh_recorder_tick_sent_after_min ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-09-13 16:55:35 -07:00
let bank0 = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
let prev_hash = bank0 . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank0 . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2021-09-13 16:55:35 -07:00
bank0 . ticks_per_slot ( ) ,
2019-03-29 20:00:36 -07:00
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2021-09-13 16:55:35 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank0 ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-19 16:17:36 -08:00
2022-04-21 06:05:29 -07:00
bank0 . fill_bank_with_ticks_for_tests ( ) ;
2021-09-13 16:55:35 -07:00
let bank1 = Arc ::new ( Bank ::new_from_parent ( & bank0 , & Pubkey ::default ( ) , 1 ) ) ;
// Set a working bank
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank1 , false ) ;
2021-09-13 16:55:35 -07:00
// 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 ) ;
2019-03-29 20:00:36 -07:00
assert! ( entry_receiver . try_recv ( ) . is_err ( ) ) ;
// all ticks are sent after height > min
2021-09-13 16:55:35 -07:00
let tick_height_before = poh_recorder . tick_height ( ) ;
2019-03-29 20:00:36 -07:00
poh_recorder . tick ( ) ;
2021-09-13 16:55:35 -07:00
assert_eq! ( poh_recorder . tick_height , tick_height_before + 1 ) ;
2019-03-29 20:00:36 -07:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
2019-09-18 12:16:22 -07:00
let mut num_entries = 0 ;
while let Ok ( ( wbank , ( _entry , _tick_height ) ) ) = entry_receiver . try_recv ( ) {
2021-09-13 16:55:35 -07:00
assert_eq! ( wbank . slot ( ) , bank1 . slot ( ) ) ;
2019-09-18 12:16:22 -07:00
num_entries + = 1 ;
}
2021-09-13 16:55:35 -07:00
// All the cached ticks, plus the new tick above should have been flushed
assert_eq! ( num_entries , num_new_ticks + 1 ) ;
2019-03-29 20:00:36 -07:00
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-02-24 08:59:49 -08:00
}
2018-12-10 20:03:04 -08:00
2019-02-24 08:59:49 -08:00
#[ test ]
fn test_poh_recorder_tick_sent_upto_and_including_max ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-08-05 06:42:38 -07:00
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2019-03-29 20:00:36 -07:00
let prev_hash = bank . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2018-09-26 05:52:13 -07:00
2021-09-13 16:55:35 -07:00
// 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 ) ;
2019-03-29 20:00:36 -07:00
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank , false ) ;
2019-03-29 20:00:36 -07:00
poh_recorder . tick ( ) ;
2018-12-12 18:52:11 -08:00
2021-09-13 16:55:35 -07:00
assert_eq! ( poh_recorder . tick_height , bank . max_tick_height ( ) + 2 ) ;
2019-03-29 20:00:36 -07:00
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
2019-09-18 12:16:22 -07:00
let mut num_entries = 0 ;
2020-06-08 17:38:14 -07:00
while entry_receiver . try_recv ( ) . is_ok ( ) {
2019-09-18 12:16:22 -07:00
num_entries + = 1 ;
}
2021-09-13 16:55:35 -07:00
// 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 ( ) ) ;
2019-03-29 20:00:36 -07:00
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-02-19 16:17:36 -08:00
}
#[ test ]
2019-02-24 08:59:49 -08:00
fn test_poh_recorder_record_to_early ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-09-13 16:55:35 -07:00
let bank0 = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
let prev_hash = bank0 . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank0 . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2021-09-13 16:55:35 -07:00
bank0 . ticks_per_slot ( ) ,
2019-03-29 20:00:36 -07:00
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2021-09-13 16:55:35 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank0 ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-19 16:17:36 -08:00
2022-04-21 06:05:29 -07:00
bank0 . fill_bank_with_ticks_for_tests ( ) ;
2021-09-13 16:55:35 -07:00
let bank1 = Arc ::new ( Bank ::new_from_parent ( & bank0 , & Pubkey ::default ( ) , 1 ) ) ;
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank1 , false ) ;
2021-09-13 16:55:35 -07:00
// Let poh_recorder tick up to bank1.tick_height() - 1
for _ in 0 .. bank1 . tick_height ( ) - 1 {
poh_recorder . tick ( )
}
2019-03-29 20:00:36 -07:00
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
2021-09-13 16:55:35 -07:00
// 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 )
) ;
2019-03-29 20:00:36 -07:00
assert! ( entry_receiver . try_recv ( ) . is_err ( ) ) ;
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-02-24 08:59:49 -08:00
}
2019-02-19 16:17:36 -08:00
2019-03-22 14:17:39 -07:00
#[ test ]
fn test_poh_recorder_record_bad_slot ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-08-05 06:42:38 -07:00
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2019-03-29 20:00:36 -07:00
let prev_hash = bank . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-03-22 14:17:39 -07:00
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank , false ) ;
2019-03-29 20:00:36 -07:00
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
2021-09-13 16:55:35 -07:00
// 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 ;
2019-03-29 20:00:36 -07:00
assert_matches! (
2021-09-13 16:55:35 -07:00
poh_recorder . record ( bad_slot , h1 , vec! [ tx . into ( ) ] ) ,
2020-01-02 19:50:43 -08:00
Err ( PohRecorderError ::MaxHeightReached )
2019-03-29 20:00:36 -07:00
) ;
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-03-22 14:17:39 -07:00
}
2019-02-24 08:59:49 -08:00
#[ test ]
fn test_poh_recorder_record_at_min_passes ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-09-13 16:55:35 -07:00
let bank0 = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
let prev_hash = bank0 . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank0 . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2021-09-13 16:55:35 -07:00
bank0 . ticks_per_slot ( ) ,
2019-03-29 20:00:36 -07:00
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2021-09-13 16:55:35 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank0 ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-19 16:17:36 -08:00
2022-04-21 06:05:29 -07:00
bank0 . fill_bank_with_ticks_for_tests ( ) ;
2021-09-13 16:55:35 -07:00
let bank1 = Arc ::new ( Bank ::new_from_parent ( & bank0 , & Pubkey ::default ( ) , 1 ) ) ;
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank1 , false ) ;
2021-09-13 16:55:35 -07:00
// 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 ) ;
2019-03-29 20:00:36 -07:00
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
2021-08-17 15:17:56 -07:00
assert! ( poh_recorder
2021-09-13 16:55:35 -07:00
. record ( bank1 . slot ( ) , h1 , vec! [ tx . into ( ) ] )
2021-08-17 15:17:56 -07:00
. is_ok ( ) ) ;
2019-03-29 20:00:36 -07:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
//tick in the cache + entry
2021-09-13 16:55:35 -07:00
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 ( ) ;
2019-09-18 12:16:22 -07:00
assert! ( ! e . is_tick ( ) ) ;
2019-03-29 20:00:36 -07:00
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-02-19 16:17:36 -08:00
}
#[ test ]
2019-02-24 08:59:49 -08:00
fn test_poh_recorder_record_at_max_fails ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-08-05 06:42:38 -07:00
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2019-03-29 20:00:36 -07:00
let prev_hash = bank . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-19 16:17:36 -08:00
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank , false ) ;
2021-09-13 16:55:35 -07:00
let num_ticks_to_max = bank . max_tick_height ( ) - poh_recorder . tick_height ;
for _ in 0 .. num_ticks_to_max {
poh_recorder . tick ( ) ;
}
2019-03-29 20:00:36 -07:00
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
2021-08-17 15:17:56 -07:00
assert! ( poh_recorder
. record ( bank . slot ( ) , h1 , vec! [ tx . into ( ) ] )
. is_err ( ) ) ;
2021-09-13 16:55:35 -07:00
for _ in 0 .. num_ticks_to_max {
let ( _bank , ( entry , _tick_height ) ) = entry_receiver . recv ( ) . unwrap ( ) ;
assert! ( entry . is_tick ( ) ) ;
}
2019-03-29 20:00:36 -07:00
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-02-24 08:59:49 -08:00
}
2019-02-19 16:17:36 -08:00
2022-06-23 12:37:38 -07:00
#[ test ]
fn test_poh_recorder_record_transaction_index ( ) {
let ledger_path = get_tmp_ledger_path! ( ) ;
{
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 ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
bank . clone ( ) ,
Some ( ( 4 , 4 ) ) ,
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
& Arc ::new ( blockstore ) ,
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank ) ) ,
& Arc ::new ( PohConfig ::default ( ) ) ,
Arc ::new ( AtomicBool ::default ( ) ) ,
) ;
poh_recorder . set_bank ( & bank , true ) ;
poh_recorder . tick ( ) ;
assert_eq! (
poh_recorder
. working_bank
. as_ref ( )
. unwrap ( )
. transaction_index
. unwrap ( ) ,
0
) ;
let tx0 = test_tx ( ) ;
let tx1 = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
let record_result = poh_recorder
. record ( bank . slot ( ) , h1 , vec! [ tx0 . into ( ) , tx1 . into ( ) ] )
. unwrap ( )
. unwrap ( ) ;
assert_eq! ( record_result , 0 ) ;
assert_eq! (
poh_recorder
. working_bank
. as_ref ( )
. unwrap ( )
. transaction_index
. unwrap ( ) ,
2
) ;
let tx = test_tx ( ) ;
let h2 = hash ( b " foobar " ) ;
let record_result = poh_recorder
. record ( bank . slot ( ) , h2 , vec! [ tx . into ( ) ] )
. unwrap ( )
. unwrap ( ) ;
assert_eq! ( record_result , 2 ) ;
assert_eq! (
poh_recorder
. working_bank
. as_ref ( )
. unwrap ( )
. transaction_index
. unwrap ( ) ,
3
) ;
}
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
}
2019-02-24 08:59:49 -08:00
#[ test ]
fn test_poh_cache_on_disconnect ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-09-13 16:55:35 -07:00
let bank0 = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
let prev_hash = bank0 . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank0 . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2021-09-13 16:55:35 -07:00
bank0 . ticks_per_slot ( ) ,
2019-03-29 20:00:36 -07:00
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2021-09-13 16:55:35 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank0 ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-19 16:17:36 -08:00
2022-04-21 06:05:29 -07:00
bank0 . fill_bank_with_ticks_for_tests ( ) ;
2021-09-13 16:55:35 -07:00
let bank1 = Arc ::new ( Bank ::new_from_parent ( & bank0 , & Pubkey ::default ( ) , 1 ) ) ;
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank1 , false ) ;
2021-09-13 16:55:35 -07:00
// 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
2019-03-29 20:00:36 -07:00
drop ( entry_receiver ) ;
poh_recorder . tick ( ) ;
2021-09-13 16:55:35 -07:00
// Check everything is cleared
2019-03-29 20:00:36 -07:00
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
2021-09-13 16:55:35 -07:00
// 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
) ;
2019-03-29 20:00:36 -07:00
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2018-09-26 05:52:13 -07:00
}
2019-03-04 07:08:22 -08:00
#[ test ]
fn test_reset_current ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2021-09-13 16:55:35 -07:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
Hash ::default ( ) ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
DEFAULT_TICKS_PER_SLOT ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::default ( ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 2 ) ;
2021-09-13 16:55:35 -07:00
poh_recorder . reset ( bank , Some ( ( 4 , 4 ) ) ) ;
2019-03-29 20:00:36 -07:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-03-04 07:08:22 -08:00
}
#[ test ]
fn test_reset_with_cached ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2021-09-13 16:55:35 -07:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
Hash ::default ( ) ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
DEFAULT_TICKS_PER_SLOT ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::default ( ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 2 ) ;
2021-09-13 16:55:35 -07:00
poh_recorder . reset ( bank , Some ( ( 4 , 4 ) ) ) ;
2019-03-29 20:00:36 -07:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-03-04 07:08:22 -08:00
}
#[ test ]
fn test_reset_to_new_value ( ) {
2019-07-17 14:10:15 -07:00
solana_logger ::setup ( ) ;
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2021-09-13 16:55:35 -07:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
Hash ::default ( ) ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
DEFAULT_TICKS_PER_SLOT ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::default ( ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
2019-10-16 12:53:11 -07:00
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 4 ) ;
assert_eq! ( poh_recorder . tick_height , 4 ) ;
2021-09-13 16:55:35 -07:00
poh_recorder . reset ( bank , Some ( ( 4 , 4 ) ) ) ; // parent slot 0 implies tick_height of 3
2019-03-29 20:00:36 -07:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
poh_recorder . tick ( ) ;
2019-10-23 12:11:04 -07:00
assert_eq! ( poh_recorder . tick_height , DEFAULT_TICKS_PER_SLOT + 1 ) ;
2019-03-29 20:00:36 -07:00
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-03-04 07:08:22 -08:00
}
2019-03-05 17:56:51 -08:00
#[ test ]
fn test_reset_clear_bank ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-08-05 06:42:38 -07:00
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
Hash ::default ( ) ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2021-09-13 16:55:35 -07:00
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank , false ) ;
2021-09-13 16:55:35 -07:00
assert_eq! ( bank . slot ( ) , 0 ) ;
poh_recorder . reset ( bank , Some ( ( 4 , 4 ) ) ) ;
2019-03-29 20:00:36 -07:00
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-03-05 17:56:51 -08:00
}
2019-03-11 13:58:23 -07:00
#[ test ]
pub fn test_clear_signal ( ) {
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-08-05 06:42:38 -07:00
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2022-01-11 02:44:46 -08:00
let ( sender , receiver ) = bounded ( 1 ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) =
PohRecorder ::new_with_clear_signal (
0 ,
Hash ::default ( ) ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2021-03-23 07:10:04 -07:00
None ,
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
& Arc ::new ( blockstore ) ,
Some ( sender ) ,
& Arc ::new ( LeaderScheduleCache ::default ( ) ) ,
& Arc ::new ( PohConfig ::default ( ) ) ,
2022-03-30 07:04:49 -07:00
None ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2021-03-23 07:10:04 -07:00
) ;
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank , false ) ;
2019-03-29 20:00:36 -07:00
poh_recorder . clear_bank ( ) ;
assert! ( receiver . try_recv ( ) . is_ok ( ) ) ;
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-03-11 13:58:23 -07:00
}
2019-03-13 14:06:12 -07:00
#[ test ]
2021-09-13 16:55:35 -07:00
fn test_poh_recorder_record_sets_start_slot ( ) {
2019-10-16 12:53:11 -07:00
solana_logger ::setup ( ) ;
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-03-29 20:00:36 -07:00
let ticks_per_slot = 5 ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo {
mut genesis_config , ..
} = create_genesis_config ( 2 ) ;
genesis_config . ticks_per_slot = ticks_per_slot ;
2021-08-05 06:42:38 -07:00
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2019-03-29 20:00:36 -07:00
let prev_hash = bank . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 4 , 4 ) ) ,
2019-03-29 20:00:36 -07:00
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-19 02:39:44 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-03-13 14:06:12 -07:00
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank , false ) ;
2019-03-29 20:00:36 -07:00
2021-09-13 16:55:35 -07:00
// 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 {
2019-03-29 20:00:36 -07:00
poh_recorder . tick ( ) ;
}
2019-03-13 14:06:12 -07:00
2019-03-29 20:00:36 -07:00
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
2021-08-17 15:17:56 -07:00
assert! ( poh_recorder
. record ( bank . slot ( ) , h1 , vec! [ tx . into ( ) ] )
. is_err ( ) ) ;
2019-03-29 20:00:36 -07:00
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
2021-09-13 16:55:35 -07:00
// 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 ( ) ) ;
2019-03-13 14:06:12 -07:00
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-03-13 14:06:12 -07:00
}
2019-03-15 13:22:16 -07:00
2020-01-13 15:55:41 -08:00
#[ test ]
fn test_reached_leader_tick ( ) {
solana_logger ::setup ( ) ;
let ledger_path = get_tmp_ledger_path! ( ) ;
{
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-08-05 06:42:38 -07:00
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2020-01-13 15:55:41 -08:00
let prev_hash = bank . last_blockhash ( ) ;
2020-02-29 00:05:35 -08:00
let leader_schedule_cache = Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank ) ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2020-01-13 15:55:41 -08:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2020-01-13 15:55:41 -08:00
None ,
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
& Arc ::new ( blockstore ) ,
2020-02-29 00:05:35 -08:00
& leader_schedule_cache ,
2020-01-13 15:55:41 -08:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2020-01-13 15:55:41 -08:00
) ;
2020-02-29 00:05:35 -08:00
let bootstrap_validator_id = leader_schedule_cache . slot_leader_at ( 0 , None ) . unwrap ( ) ;
2021-05-19 07:31:47 -07:00
assert! ( poh_recorder . reached_leader_tick ( 0 ) ) ;
2020-01-13 15:55:41 -08:00
let grace_ticks = bank . ticks_per_slot ( ) * MAX_GRACE_SLOTS ;
let new_tick_height = NUM_CONSECUTIVE_LEADER_SLOTS * bank . ticks_per_slot ( ) ;
for _ in 0 .. new_tick_height {
poh_recorder . tick ( ) ;
}
poh_recorder . grace_ticks = grace_ticks ;
2020-08-19 01:16:24 -07:00
// False, because the Poh was reset on slot 0, which
// is a block produced by the previous leader, so a grace
// period must be given
assert! ( ! poh_recorder . reached_leader_tick ( new_tick_height + grace_ticks ) ) ;
// Tick `NUM_CONSECUTIVE_LEADER_SLOTS` more times
let new_tick_height = 2 * NUM_CONSECUTIVE_LEADER_SLOTS * bank . ticks_per_slot ( ) ;
for _ in 0 .. new_tick_height {
poh_recorder . tick ( ) ;
}
// True, because
// 1) the Poh was reset on slot 0
// 2) Our slot starts at 2 * NUM_CONSECUTIVE_LEADER_SLOTS, which means
// none of the previous leader's `NUM_CONSECUTIVE_LEADER_SLOTS` were slots
// this Poh built on (previous leader was on different fork). Thus, skip the
// grace period.
assert! ( poh_recorder . reached_leader_tick ( new_tick_height + grace_ticks ) ) ;
2020-02-29 00:05:35 -08:00
// From the bootstrap validator's perspective, it should have reached
2020-08-19 01:16:24 -07:00
// the tick because the previous slot was also it's own slot (all slots
// belong to the bootstrap leader b/c it's the only staked node!), and
// validators don't give grace periods if previous slot was also their own.
2020-02-29 00:05:35 -08:00
poh_recorder . id = bootstrap_validator_id ;
assert! ( poh_recorder . reached_leader_tick ( new_tick_height + grace_ticks ) ) ;
2020-01-13 15:55:41 -08:00
}
}
2019-03-15 13:22:16 -07:00
#[ test ]
2019-10-16 12:53:11 -07:00
fn test_reached_leader_slot ( ) {
2019-07-17 14:10:15 -07:00
solana_logger ::setup ( ) ;
2019-03-29 20:00:36 -07:00
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-09-13 16:55:35 -07:00
let bank0 = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
let prev_hash = bank0 . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-03-29 20:00:36 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank0 . clone ( ) ,
2019-03-29 20:00:36 -07:00
None ,
2021-09-13 16:55:35 -07:00
bank0 . ticks_per_slot ( ) ,
2019-03-29 20:00:36 -07:00
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2021-09-13 16:55:35 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank0 ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-03-15 13:22:16 -07:00
2019-10-16 12:53:11 -07:00
// Test that with no next leader slot, we don't reach the leader slot
2022-01-22 18:28:50 -08:00
assert_eq! (
poh_recorder . reached_leader_slot ( ) ,
PohLeaderStatus ::NotReached
) ;
2019-03-15 13:22:16 -07:00
2019-10-16 12:53:11 -07:00
// Test that with no next leader slot in reset(), we don't reach the leader slot
2021-09-13 16:55:35 -07:00
assert_eq! ( bank0 . slot ( ) , 0 ) ;
poh_recorder . reset ( bank0 . clone ( ) , None ) ;
2022-01-22 18:28:50 -08:00
assert_eq! (
poh_recorder . reached_leader_slot ( ) ,
PohLeaderStatus ::NotReached
) ;
2019-03-15 13:22:16 -07:00
2019-10-16 12:53:11 -07:00
// Provide a leader slot one slot down
2021-09-13 16:55:35 -07:00
poh_recorder . reset ( bank0 . clone ( ) , Some ( ( 2 , 2 ) ) ) ;
2019-03-15 13:22:16 -07:00
2019-03-29 20:00:36 -07:00
let init_ticks = poh_recorder . tick_height ( ) ;
2019-03-15 13:22:16 -07:00
2019-03-29 20:00:36 -07:00
// Send one slot worth of ticks
2021-09-13 16:55:35 -07:00
for _ in 0 .. bank0 . ticks_per_slot ( ) {
2019-03-29 20:00:36 -07:00
poh_recorder . tick ( ) ;
}
2019-03-15 13:22:16 -07:00
2019-03-29 20:00:36 -07:00
// Tick should be recorded
assert_eq! (
poh_recorder . tick_height ( ) ,
2021-09-13 16:55:35 -07:00
init_ticks + bank0 . ticks_per_slot ( )
2019-03-29 20:00:36 -07:00
) ;
2019-03-15 13:22:16 -07:00
2020-12-13 17:26:34 -08:00
let parent_meta = SlotMeta {
received : 1 ,
.. SlotMeta ::default ( )
} ;
2020-01-13 15:55:41 -08:00
poh_recorder
. blockstore
. put_meta_bytes ( 0 , & serialize ( & parent_meta ) . unwrap ( ) )
. unwrap ( ) ;
2019-10-16 12:53:11 -07:00
// Test that we don't reach the leader slot because of grace ticks
2022-01-22 18:28:50 -08:00
assert_eq! (
poh_recorder . reached_leader_slot ( ) ,
PohLeaderStatus ::NotReached
) ;
2019-03-15 13:22:16 -07:00
2019-07-17 14:10:15 -07:00
// reset poh now. we should immediately be leader
2021-09-13 16:55:35 -07:00
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 ) ) ) ;
2022-01-22 18:28:50 -08:00
assert_eq! (
poh_recorder . reached_leader_slot ( ) ,
PohLeaderStatus ::Reached {
poh_slot : 2 ,
parent_slot : 1 ,
}
) ;
2019-10-16 12:53:11 -07:00
// Now test that with grace ticks we can reach leader slot
// Set the leader slot one slot down
2021-09-13 16:55:35 -07:00
poh_recorder . reset ( bank1 . clone ( ) , Some ( ( 3 , 3 ) ) ) ;
2019-03-15 13:22:16 -07:00
2019-07-17 14:10:15 -07:00
// Send one slot worth of ticks ("skips" slot 2)
2021-09-13 16:55:35 -07:00
for _ in 0 .. bank1 . ticks_per_slot ( ) {
2019-03-29 20:00:36 -07:00
poh_recorder . tick ( ) ;
}
// We are not the leader yet, as expected
2022-01-22 18:28:50 -08:00
assert_eq! (
poh_recorder . reached_leader_slot ( ) ,
PohLeaderStatus ::NotReached
) ;
2019-03-15 13:22:16 -07:00
2019-10-16 12:53:11 -07:00
// Send the grace ticks
2021-09-13 16:55:35 -07:00
for _ in 0 .. bank1 . ticks_per_slot ( ) / GRACE_TICKS_FACTOR {
2019-03-29 20:00:36 -07:00
poh_recorder . tick ( ) ;
}
2019-03-15 13:22:16 -07:00
2019-03-29 20:00:36 -07:00
// We should be the leader now
2022-01-22 18:28:50 -08:00
// without sending more ticks, we should be leader now
assert_eq! (
poh_recorder . reached_leader_slot ( ) ,
PohLeaderStatus ::Reached {
poh_slot : 3 ,
parent_slot : 1 ,
}
) ;
2019-03-15 13:22:16 -07:00
2019-03-29 20:00:36 -07:00
// Let's test that correct grace ticks are reported
2019-10-16 12:53:11 -07:00
// Set the leader slot one slot down
2021-09-13 16:55:35 -07:00
let bank2 = Arc ::new ( Bank ::new_from_parent ( & bank1 , & Pubkey ::default ( ) , 2 ) ) ;
poh_recorder . reset ( bank2 . clone ( ) , Some ( ( 4 , 4 ) ) ) ;
2019-03-18 13:24:07 -07:00
2019-07-17 14:10:15 -07:00
// send ticks for a slot
2021-09-13 16:55:35 -07:00
for _ in 0 .. bank1 . ticks_per_slot ( ) {
2019-03-29 20:00:36 -07:00
poh_recorder . tick ( ) ;
}
2019-03-18 13:24:07 -07:00
2019-03-29 20:00:36 -07:00
// We are not the leader yet, as expected
2022-01-22 18:28:50 -08:00
assert_eq! (
poh_recorder . reached_leader_slot ( ) ,
PohLeaderStatus ::NotReached
) ;
2021-09-13 16:55:35 -07:00
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 ) ) ) ;
2019-07-17 14:10:15 -07:00
2019-03-29 20:00:36 -07:00
// without sending more ticks, we should be leader now
2022-01-22 18:28:50 -08:00
assert_eq! (
poh_recorder . reached_leader_slot ( ) ,
PohLeaderStatus ::Reached {
poh_slot : 4 ,
parent_slot : 3 ,
}
) ;
2019-03-29 20:00:36 -07:00
// Let's test that if a node overshoots the ticks for its target
2019-10-16 12:53:11 -07:00
// leader slot, reached_leader_slot() will return true, because it's overdue
// Set the leader slot one slot down
2021-09-13 16:55:35 -07:00
let bank4 = Arc ::new ( Bank ::new_from_parent ( & bank3 , & Pubkey ::default ( ) , 4 ) ) ;
poh_recorder . reset ( bank4 . clone ( ) , Some ( ( 5 , 5 ) ) ) ;
2019-03-19 14:49:36 -07:00
2019-10-16 12:53:11 -07:00
// Overshoot ticks for the slot
let overshoot_factor = 4 ;
2021-09-13 16:55:35 -07:00
for _ in 0 .. overshoot_factor * bank4 . ticks_per_slot ( ) {
2019-03-29 20:00:36 -07:00
poh_recorder . tick ( ) ;
}
2019-03-19 14:49:36 -07:00
2019-07-09 15:36:30 -07:00
// We are overdue to lead
2022-01-22 18:28:50 -08:00
assert_eq! (
poh_recorder . reached_leader_slot ( ) ,
PohLeaderStatus ::Reached {
poh_slot : 9 ,
parent_slot : 4 ,
}
) ;
2019-03-29 20:00:36 -07:00
}
2020-01-13 13:13:52 -08:00
Blockstore ::destroy ( & ledger_path ) . unwrap ( ) ;
2019-03-15 13:22:16 -07:00
}
2019-04-22 19:49:32 -07:00
#[ test ]
fn test_would_be_leader_soon ( ) {
let ledger_path = get_tmp_ledger_path! ( ) ;
{
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-08-05 06:42:38 -07:00
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2019-04-22 19:49:32 -07:00
let prev_hash = bank . last_blockhash ( ) ;
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-04-22 19:49:32 -07:00
0 ,
prev_hash ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-04-22 19:49:32 -07:00
None ,
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-04-22 19:49:32 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank ) ) ,
2019-05-18 14:01:36 -07:00
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-04-22 19:49:32 -07:00
) ;
// Test that with no leader slot, we don't reach the leader tick
2021-05-19 07:31:47 -07:00
assert! ( ! poh_recorder . would_be_leader ( 2 * bank . ticks_per_slot ( ) ) ) ;
2019-04-22 19:49:32 -07:00
2021-09-13 16:55:35 -07:00
assert_eq! ( bank . slot ( ) , 0 ) ;
poh_recorder . reset ( bank . clone ( ) , None ) ;
2019-04-22 19:49:32 -07:00
2021-05-19 07:31:47 -07:00
assert! ( ! poh_recorder . would_be_leader ( 2 * bank . ticks_per_slot ( ) ) ) ;
2019-04-22 19:49:32 -07:00
// We reset with leader slot after 3 slots
2019-07-26 11:33:51 -07:00
let bank_slot = bank . slot ( ) + 3 ;
2021-09-13 16:55:35 -07:00
poh_recorder . reset ( bank . clone ( ) , Some ( ( bank_slot , bank_slot ) ) ) ;
2019-04-22 19:49:32 -07:00
// Test that the node won't be leader in next 2 slots
2021-05-19 07:31:47 -07:00
assert! ( ! poh_recorder . would_be_leader ( 2 * bank . ticks_per_slot ( ) ) ) ;
2019-04-22 19:49:32 -07:00
// Test that the node will be leader in next 3 slots
2021-05-19 07:31:47 -07:00
assert! ( poh_recorder . would_be_leader ( 3 * bank . ticks_per_slot ( ) ) ) ;
2019-04-22 19:49:32 -07:00
2021-05-19 07:31:47 -07:00
assert! ( ! poh_recorder . would_be_leader ( 2 * bank . ticks_per_slot ( ) ) ) ;
2019-04-22 19:49:32 -07:00
2019-07-25 11:08:44 -07:00
// Move the bank up a slot (so that max_tick_height > slot 0's tick_height)
let bank = Arc ::new ( Bank ::new_from_parent ( & bank , & Pubkey ::default ( ) , 1 ) ) ;
2019-04-22 19:49:32 -07:00
// If we set the working bank, the node should be leader within next 2 slots
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank , false ) ;
2021-05-19 07:31:47 -07:00
assert! ( poh_recorder . would_be_leader ( 2 * bank . ticks_per_slot ( ) ) ) ;
2019-04-22 19:49:32 -07:00
}
}
2019-07-25 11:08:44 -07:00
#[ test ]
fn test_flush_virtual_ticks ( ) {
let ledger_path = get_tmp_ledger_path! ( ) ;
{
// test that virtual ticks are flushed into a newly set bank asap
2020-01-13 13:13:52 -08:00
let blockstore = Blockstore ::open ( & ledger_path )
. expect ( " Expected to be able to open database ledger " ) ;
2019-11-08 20:56:57 -08:00
let GenesisConfigInfo { genesis_config , .. } = create_genesis_config ( 2 ) ;
2021-08-05 06:42:38 -07:00
let bank = Arc ::new ( Bank ::new_for_tests ( & genesis_config ) ) ;
2019-11-08 20:56:57 -08:00
let genesis_hash = bank . last_blockhash ( ) ;
2019-07-25 11:08:44 -07:00
2021-03-23 07:10:04 -07:00
let ( mut poh_recorder , _entry_receiver , _record_receiver ) = PohRecorder ::new (
2019-07-25 11:08:44 -07:00
0 ,
bank . last_blockhash ( ) ,
2021-09-13 16:55:35 -07:00
bank . clone ( ) ,
2019-07-26 11:33:51 -07:00
Some ( ( 2 , 2 ) ) ,
2019-07-25 11:08:44 -07:00
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
2020-01-13 13:13:52 -08:00
& Arc ::new ( blockstore ) ,
2019-07-25 11:08:44 -07:00
& Arc ::new ( LeaderScheduleCache ::new_from_bank ( & bank ) ) ,
& Arc ::new ( PohConfig ::default ( ) ) ,
2021-04-14 10:07:21 -07:00
Arc ::new ( AtomicBool ::default ( ) ) ,
2019-07-25 11:08:44 -07:00
) ;
//create a new bank
let bank = Arc ::new ( Bank ::new_from_parent ( & bank , & Pubkey ::default ( ) , 2 ) ) ;
2022-04-21 06:05:29 -07:00
// add virtual ticks into poh for slots 0, 1, and 2
for _ in 0 .. ( bank . ticks_per_slot ( ) * 3 ) {
2019-07-25 11:08:44 -07:00
poh_recorder . tick ( ) ;
}
2022-06-23 12:37:38 -07:00
poh_recorder . set_bank ( & bank , false ) ;
2022-04-21 06:05:29 -07:00
assert! ( ! bank . is_hash_valid_for_age ( & genesis_hash , 0 ) ) ;
assert! ( bank . is_hash_valid_for_age ( & genesis_hash , 1 ) ) ;
2019-07-25 11:08:44 -07:00
}
}
2019-07-26 11:33:51 -07:00
#[ test ]
2019-10-16 12:53:11 -07:00
fn test_compute_leader_slot_tick_heights ( ) {
2019-07-26 11:33:51 -07:00
assert_eq! (
2019-10-16 12:53:11 -07:00
PohRecorder ::compute_leader_slot_tick_heights ( None , 0 ) ,
2019-07-26 11:33:51 -07:00
( None , 0 , 0 )
) ;
assert_eq! (
2019-10-16 12:53:11 -07:00
PohRecorder ::compute_leader_slot_tick_heights ( Some ( ( 4 , 4 ) ) , 8 ) ,
( Some ( 37 ) , 40 , 4 )
2019-07-26 11:33:51 -07:00
) ;
assert_eq! (
2019-10-16 12:53:11 -07:00
PohRecorder ::compute_leader_slot_tick_heights ( Some ( ( 4 , 7 ) ) , 8 ) ,
2019-11-19 17:55:42 -08:00
( Some ( 49 ) , 64 , 2 * 8 )
2019-07-26 11:33:51 -07:00
) ;
assert_eq! (
2019-10-16 12:53:11 -07:00
PohRecorder ::compute_leader_slot_tick_heights ( Some ( ( 6 , 7 ) ) , 8 ) ,
( Some ( 57 ) , 64 , 8 )
2019-07-26 11:33:51 -07:00
) ;
assert_eq! (
2019-10-16 12:53:11 -07:00
PohRecorder ::compute_leader_slot_tick_heights ( Some ( ( 6 , 7 ) ) , 4 ) ,
( Some ( 29 ) , 32 , 4 )
2019-07-26 11:33:51 -07:00
) ;
}
2018-09-26 05:52:13 -07:00
}