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
//!
2021-03-26 11:54:16 -07:00
use crate ::poh_service ::PohService ;
2020-01-13 13:13:52 -08:00
use solana_ledger ::blockstore ::Blockstore ;
2019-10-18 09:28:51 -07:00
use solana_ledger ::entry ::Entry ;
use solana_ledger ::leader_schedule_cache ::LeaderScheduleCache ;
use solana_ledger ::poh ::Poh ;
2019-02-18 22:26:22 -08:00
use solana_runtime ::bank ::Bank ;
2019-09-06 14:30:56 -07:00
pub use solana_sdk ::clock ::Slot ;
use solana_sdk ::clock ::NUM_CONSECUTIVE_LEADER_SLOTS ;
2018-11-16 08:04:46 -08:00
use solana_sdk ::hash ::Hash ;
2019-05-18 14:01:36 -07:00
use solana_sdk ::poh_config ::PohConfig ;
2019-03-20 13:49:46 -07:00
use solana_sdk ::pubkey ::Pubkey ;
2019-05-22 15:54:24 -07:00
use solana_sdk ::timing ;
2018-11-29 16:18:47 -08:00
use solana_sdk ::transaction ::Transaction ;
2019-07-26 11:33:51 -07:00
use std ::cmp ;
2019-09-18 12:16:22 -07:00
use std ::sync ::mpsc ::{ channel , Receiver , SendError , Sender , SyncSender } ;
2019-05-18 14:01:36 -07:00
use std ::sync ::{ Arc , Mutex } ;
2019-05-22 15:54:24 -07:00
use std ::time ::Instant ;
2020-01-02 19:50:43 -08:00
use thiserror ::Error ;
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-03-15 17:11:15 -07:00
pub type BankStart = ( Arc < Bank > , Arc < Instant > ) ;
2019-03-03 16:44:06 -08:00
2021-03-23 07:10:04 -07:00
pub struct Record {
pub mixin : Hash ,
pub transactions : Vec < Transaction > ,
pub slot : Slot ,
pub sender : Sender < Result < ( ) > > ,
}
impl Record {
pub fn new (
mixin : Hash ,
transactions : Vec < Transaction > ,
slot : Slot ,
sender : Sender < Result < ( ) > > ,
) -> Self {
Self {
mixin ,
transactions ,
slot ,
sender ,
}
}
}
pub struct TransactionRecorder {
// shared by all users of PohRecorder
pub record_sender : Sender < Record > ,
// unique to this caller
pub result_sender : Sender < Result < ( ) > > ,
pub result_receiver : Receiver < Result < ( ) > > ,
}
impl Clone for TransactionRecorder {
fn clone ( & self ) -> Self {
TransactionRecorder ::new ( self . record_sender . clone ( ) )
}
}
impl TransactionRecorder {
pub fn new ( record_sender : Sender < Record > ) -> Self {
let ( result_sender , result_receiver ) = channel ( ) ;
Self {
// shared
record_sender ,
// unique to this caller
result_sender ,
result_receiver ,
}
}
pub fn record (
& self ,
bank_slot : Slot ,
mixin : Hash ,
transactions : Vec < Transaction > ,
) -> Result < ( ) > {
let res = self . record_sender . send ( Record ::new (
mixin ,
transactions ,
bank_slot ,
self . result_sender . clone ( ) ,
) ) ;
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-03-23 07:10:04 -07:00
let res = self
. result_receiver
2021-03-25 19:31:07 -07:00
. recv_timeout ( std ::time ::Duration ::from_millis ( 5000 ) ) ;
2021-03-23 07:10:04 -07:00
match res {
Err ( _err ) = > Err ( PohRecorderError ::MaxHeightReached ) ,
Ok ( result ) = > result ,
}
}
}
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 ,
2018-10-18 22:57:48 -07:00
}
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 ,
2019-04-09 17:53:04 -07:00
clear_bank_signal : Option < SyncSender < bool > > ,
2019-10-16 12:53:11 -07:00
start_slot : Slot , // parent slot
start_tick_height : u64 , // first tick_height this recorder will observe
tick_cache : Vec < ( Entry , u64 ) > , // cache of entry and its tick_height
2019-02-24 08:59:49 -08:00
working_bank : Option < WorkingBank > ,
2019-09-18 12:16:22 -07:00
sender : Sender < WorkingBankEntry > ,
2019-10-16 12:53:11 -07:00
leader_first_tick_height : Option < u64 > ,
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 ,
prepare_send_us : u64 ,
send_us : u64 ,
2021-03-05 16:01:21 -08:00
tick_lock_contention_us : u64 ,
tick_overhead_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 ,
2021-03-22 13:35:06 -07:00
ticks_from_record : u64 ,
2021-03-17 08:38:26 -07:00
last_metric : Instant ,
2021-03-23 07:10:04 -07:00
record_sender : Sender < Record > ,
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 ( ) ) ;
2019-10-16 12:53:11 -07:00
let ( leader_first_tick_height , 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 ;
2019-10-16 12:53:11 -07:00
self . leader_first_tick_height = leader_first_tick_height ;
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 {
let _ = signal . try_send ( true ) ;
}
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 ( )
2019-10-16 12:53:11 -07:00
| | self
. leader_first_tick_height
. map_or ( false , | leader_first_tick_height | {
let ideal_leader_tick_height =
leader_first_tick_height . saturating_sub ( self . grace_ticks ) ;
self . tick_height + within_next_n_ticks > = ideal_leader_tick_height
& & self . tick_height < = self . leader_last_tick_height
} )
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 > {
2019-10-16 12:53:11 -07:00
let current_slot = self . tick_height . saturating_sub ( 1 ) / self . ticks_per_slot ;
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 > {
self . working_bank
. as_ref ( )
. map ( | w | ( w . bank . clone ( ) , w . start . clone ( ) ) )
2019-03-03 16:44:06 -08:00
}
2019-03-12 17:42:53 -07:00
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 {
TransactionRecorder ::new ( self . record_sender . clone ( ) )
}
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
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
}
}
2020-01-13 15:55:41 -08:00
fn reached_leader_tick ( & self , leader_first_tick_height : u64 ) -> bool {
let target_tick_height = leader_first_tick_height . saturating_sub ( 1 ) ;
let ideal_target_tick_height = target_tick_height . saturating_sub ( self . grace_ticks ) ;
let current_slot = self . tick_height / self . ticks_per_slot ;
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
| | self . start_tick_height + self . grace_ticks = = leader_first_tick_height
| | ( self . tick_height > = ideal_target_tick_height
2020-02-29 00:05:35 -08:00
& & ( self . prev_slot_was_mine ( current_slot )
2020-08-19 01:16:24 -07:00
| | ! self . is_same_fork_as_previous_leader ( current_slot ) ) )
2020-01-13 15:55:41 -08:00
}
2021-03-24 23:41:52 -07:00
pub fn last_reset_slot ( & self ) -> Slot {
self . start_slot
}
2019-10-16 12:53:11 -07:00
/// returns if leader slot has been reached, how many grace ticks were afforded,
2019-07-09 15:36:30 -07:00
/// imputed leader_slot and self.start_slot
2019-10-16 12:53:11 -07:00
/// reached_leader_slot() == true means "ready for a bank"
pub fn reached_leader_slot ( & self ) -> ( bool , u64 , Slot , Slot ) {
2019-07-09 15:36:30 -07:00
trace! (
2019-10-16 12:53:11 -07:00
" tick_height {}, start_tick_height {}, leader_first_tick_height {:?}, 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 ,
self . leader_first_tick_height ,
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 ;
let next_leader_slot = ( next_tick_height - 1 ) / self . ticks_per_slot ;
if let Some ( leader_first_tick_height ) = self . leader_first_tick_height {
let target_tick_height = leader_first_tick_height . saturating_sub ( 1 ) ;
2020-01-13 15:55:41 -08:00
if self . reached_leader_tick ( leader_first_tick_height ) {
2019-10-16 12:53:11 -07:00
assert! ( next_tick_height > = self . start_tick_height ) ;
let ideal_target_tick_height = target_tick_height . saturating_sub ( self . grace_ticks ) ;
2019-07-17 14:10:15 -07:00
2019-07-09 15:36:30 -07:00
return (
true ,
2019-10-16 12:53:11 -07:00
self . tick_height . saturating_sub ( ideal_target_tick_height ) ,
next_leader_slot ,
2019-07-09 15:36:30 -07:00
self . start_slot ,
) ;
}
}
2019-10-16 12:53:11 -07:00
( false , 0 , next_leader_slot , self . start_slot )
2019-03-15 13:22:16 -07:00
}
2019-10-16 12:53:11 -07:00
// returns (leader_first_tick_height, leader_last_tick_height, grace_ticks) given the next
// 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
) ;
2019-10-16 12:53:11 -07:00
(
Some ( leader_first_tick_height + grace_ticks ) ,
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
2019-07-26 11:33:51 -07:00
pub fn reset (
& mut self ,
blockhash : Hash ,
start_slot : Slot ,
next_leader_slot : Option < ( Slot , Slot ) > ,
) {
2019-03-05 17:56:51 -08:00
self . clear_bank ( ) ;
2019-02-19 16:17:36 -08:00
let mut cache = vec! [ ] ;
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: {},{} " ,
poh_hash , self . tick_height , self . start_slot , blockhash , start_slot
) ;
2019-05-18 14:01:36 -07:00
2019-02-24 08:59:49 -08:00
std ::mem ::swap ( & mut cache , & mut self . tick_cache ) ;
2019-07-17 14:10:15 -07:00
2019-03-12 17:42:53 -07:00
self . start_slot = start_slot ;
2019-10-16 12:53:11 -07:00
self . tick_height = ( start_slot + 1 ) * self . ticks_per_slot ;
self . start_tick_height = self . tick_height + 1 ;
2019-07-17 14:10:15 -07:00
2019-10-16 12:53:11 -07:00
let ( leader_first_tick_height , 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 ;
2019-10-16 12:53:11 -07:00
self . leader_first_tick_height = leader_first_tick_height ;
self . leader_last_tick_height = leader_last_tick_height ;
2019-02-24 08:59:49 -08:00
}
pub fn set_working_bank ( & mut self , working_bank : WorkingBank ) {
2019-02-26 21:57:45 -08:00
trace! ( " new working bank " ) ;
2019-07-09 15:36:30 -07:00
assert_eq! ( working_bank . bank . ticks_per_slot ( ) , self . ticks_per_slot ( ) ) ;
2019-02-24 08:59:49 -08:00
self . working_bank = Some ( working_bank ) ;
2021-03-15 17:11:15 -07:00
// TODO: adjust the working_bank.start time based on number of ticks
// that have already elapsed based on current tick height.
2019-07-25 11:08:44 -07:00
let _ = self . flush_cache ( false ) ;
2019-02-24 08:59:49 -08:00
}
2021-03-15 17:11:15 -07:00
2019-03-03 16:44:06 -08:00
pub fn set_bank ( & mut self , bank : & Arc < Bank > ) {
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 ( ) ,
2019-03-03 16:44:06 -08:00
} ;
self . set_working_bank ( working_bank ) ;
}
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 ( )
) ;
2019-10-16 12:53:11 -07:00
let working_slot =
( working_bank . max_tick_height / self . ticks_per_slot ) . saturating_sub ( 1 ) ;
self . start_slot = working_slot ;
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
}
2019-02-24 08:59:49 -08:00
pub fn tick ( & mut self ) {
2019-05-22 15:54:24 -07:00
let now = Instant ::now ( ) ;
2021-03-26 11:54:16 -07:00
let ( poh_entry , target_time ) = {
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 )
} ;
2021-03-05 16:01:21 -08:00
self . tick_lock_contention_us + = timing ::duration_as_us ( & now . elapsed ( ) ) ;
2019-05-22 15:54:24 -07:00
let now = Instant ::now ( ) ;
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 ) ;
2019-05-03 16:41:19 -07:00
2019-10-16 12:53:11 -07:00
if self . leader_first_tick_height . is_none ( ) {
2021-03-05 16:01:21 -08:00
self . tick_overhead_us + = timing ::duration_as_us ( & now . elapsed ( ) ) ;
2019-05-18 14:01:36 -07:00
return ;
}
2019-03-15 13:22:16 -07:00
2019-05-18 14:01:36 -07:00
let entry = Entry {
num_hashes : poh_entry . num_hashes ,
hash : poh_entry . hash ,
transactions : vec ! [ ] ,
} ;
2021-03-19 07:48:55 -07:00
self . tick_overhead_us + = timing ::duration_as_us ( & now . elapsed ( ) ) ;
2019-05-18 14:01:36 -07:00
2021-03-19 07:48:55 -07:00
let now = Instant ::now ( ) ;
2019-05-18 14:01:36 -07:00
self . tick_cache . push ( ( entry , self . tick_height ) ) ;
let _ = self . flush_cache ( true ) ;
2021-03-19 07:48:55 -07:00
self . flush_cache_tick_us + = timing ::duration_as_us ( & now . elapsed ( ) ) ;
2021-03-26 11:54:16 -07: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.
let started_waiting = Instant ::now ( ) ;
while Instant ::now ( ) < target_time {
// TODO: a caller could possibly desire to reset or record while we're spinning here
std ::hint ::spin_loop ( ) ;
}
self . total_sleep_us + = started_waiting . elapsed ( ) . as_nanos ( ) as u64 / 1000 ;
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 ) ,
( " prepare_send_us " , self . prepare_send_us , i64 ) ,
( " send_us " , self . send_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
( " tick_overhead " , self . tick_overhead_us , i64 ) ,
(
" record_lock_contention " ,
self . record_lock_contention_us ,
i64
) ,
) ;
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 ;
self . tick_overhead_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 ;
self . prepare_send_us = 0 ;
self . send_us = 0 ;
2021-03-22 13:35:06 -07:00
self . ticks_from_record = 0 ;
2021-03-17 08:38:26 -07:00
self . last_metric = Instant ::now ( ) ;
}
2018-09-26 05:52:13 -07:00
}
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 ,
transactions : Vec < Transaction > ,
) -> Result < ( ) > {
// 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 " ) ;
2021-03-17 08:38:26 -07:00
self . report_metrics ( bank_slot ) ;
2019-05-18 14:01:36 -07:00
loop {
2021-03-19 07:48:55 -07:00
let now = Instant ::now ( ) ;
2019-05-18 14:01:36 -07:00
self . flush_cache ( false ) ? ;
2021-03-19 07:48:55 -07:00
self . flush_cache_no_tick_us + = timing ::duration_as_us ( & now . elapsed ( ) ) ;
2019-05-18 14:01:36 -07:00
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 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
}
2019-10-30 18:55:29 -07:00
{
let now = Instant ::now ( ) ;
let mut poh_lock = self . poh . lock ( ) . unwrap ( ) ;
2021-03-05 16:01:21 -08:00
self . record_lock_contention_us + = timing ::duration_as_us ( & now . elapsed ( ) ) ;
2019-10-30 18:55:29 -07:00
let now = Instant ::now ( ) ;
let res = poh_lock . record ( mixin ) ;
2021-03-17 08:37:20 -07:00
drop ( poh_lock ) ;
2021-03-05 16:01:21 -08:00
self . record_us + = timing ::duration_as_us ( & now . elapsed ( ) ) ;
2021-03-19 07:48:55 -07:00
let now = Instant ::now ( ) ;
2019-10-30 18:55:29 -07:00
if let Some ( poh_entry ) = res {
let entry = Entry {
num_hashes : poh_entry . num_hashes ,
hash : poh_entry . hash ,
transactions ,
} ;
2021-03-19 07:48:55 -07:00
let bank_clone = working_bank . bank . clone ( ) ;
self . prepare_send_us + = timing ::duration_as_us ( & now . elapsed ( ) ) ;
let now = Instant ::now ( ) ;
self . sender . send ( ( bank_clone , ( entry , self . tick_height ) ) ) ? ;
self . send_us + = timing ::duration_as_us ( & now . elapsed ( ) ) ;
2019-10-30 18:55:29 -07:00
return Ok ( ( ) ) ;
}
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 ,
2019-07-09 15:36:30 -07:00
start_slot : Slot ,
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 > ,
2019-04-09 17:53:04 -07:00
clear_bank_signal : Option < SyncSender < 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 > ,
2021-03-23 07:10:04 -07: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 ,
) ;
2019-03-03 16:44:06 -08:00
let ( sender , receiver ) = channel ( ) ;
2021-03-23 07:10:04 -07:00
let ( record_sender , record_receiver ) = channel ( ) ;
2019-10-16 12:53:11 -07:00
let ( leader_first_tick_height , leader_last_tick_height , grace_ticks ) =
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 ,
2019-04-09 17:53:04 -07:00
clear_bank_signal ,
2019-03-12 17:42:53 -07:00
start_slot ,
2019-10-16 12:53:11 -07:00
start_tick_height : tick_height + 1 ,
leader_first_tick_height ,
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 ,
prepare_send_us : 0 ,
send_us : 0 ,
2021-03-05 16:01:21 -08:00
tick_lock_contention_us : 0 ,
record_us : 0 ,
tick_overhead_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 ,
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
pub fn new (
tick_height : u64 ,
last_entry_hash : Hash ,
2019-07-09 15:36:30 -07:00
start_slot : Slot ,
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-03-23 07:10:04 -07:00
) -> ( Self , Receiver < WorkingBankEntry > , Receiver < Record > ) {
2019-04-09 17:53:04 -07:00
Self ::new_with_clear_signal (
tick_height ,
last_entry_hash ,
start_slot ,
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 ,
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-03-15 17:11:15 -07:00
// Filters the return result of PohRecorder::bank_start(), returns the bank
// if it's still processing transactions
pub fn get_bank_still_processing_txs ( bank_start : & Option < BankStart > ) -> Option < & Arc < Bank > > {
bank_start . as_ref ( ) . and_then ( | ( bank , bank_creation_time ) | {
if Bank ::should_bank_still_be_processing_txs ( bank_creation_time , bank . ns_per_slot ) {
Some ( bank )
} else {
None
}
} )
}
2021-02-12 03:27:37 -08:00
#[ cfg(test) ]
pub fn schedule_dummy_max_height_reached_failure ( & mut self ) {
self . reset ( Hash ::default ( ) , 1 , None ) ;
}
2018-09-26 05:52:13 -07:00
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2020-01-13 15:55:41 -08:00
use bincode ::serialize ;
2020-03-21 10:54:40 -07:00
use solana_ledger ::genesis_utils ::{ create_genesis_config , GenesisConfigInfo } ;
2020-01-13 15:55:41 -08:00
use solana_ledger ::{ blockstore ::Blockstore , blockstore_meta ::SlotMeta , get_tmp_ledger_path } ;
2019-11-06 10:52:30 -08:00
use solana_perf ::test_tx ::test_tx ;
2019-09-06 14:30:56 -07:00
use solana_sdk ::clock ::DEFAULT_TICKS_PER_SLOT ;
2018-11-16 08:04:46 -08:00
use solana_sdk ::hash ::hash ;
2019-03-11 13:58:23 -07:00
use std ::sync ::mpsc ::sync_channel ;
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-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 ,
0 ,
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 ( ) ) ,
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-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 ,
0 ,
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 ( ) ) ,
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-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 ( ) ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 1 ) ;
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( Hash ::default ( ) , 0 , 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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-24 08:59:49 -08:00
2021-03-15 17:11:15 -07:00
let start = Arc ::new ( Instant ::now ( ) ) ;
2019-03-29 20:00:36 -07:00
let working_bank = WorkingBank {
bank ,
2021-03-15 17:11:15 -07:00
start ,
2019-03-29 20:00:36 -07:00
min_tick_height : 2 ,
max_tick_height : 3 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-19 16:17:36 -08:00
2021-03-15 17:11:15 -07:00
let start = Arc ::new ( Instant ::now ( ) ) ;
2019-03-29 20:00:36 -07:00
let working_bank = WorkingBank {
bank : bank . clone ( ) ,
2021-03-15 17:11:15 -07:00
start ,
2019-03-29 20:00:36 -07:00
min_tick_height : 2 ,
max_tick_height : 3 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
//tick height equal to min_tick_height
//no tick has been sent
assert_eq! ( poh_recorder . tick_cache . last ( ) . unwrap ( ) . 1 , 2 ) ;
assert! ( entry_receiver . try_recv ( ) . is_err ( ) ) ;
// all ticks are sent after height > min
poh_recorder . tick ( ) ;
2019-05-18 14:01:36 -07:00
assert_eq! ( poh_recorder . tick_height , 3 ) ;
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 ( ) {
assert_eq! ( wbank . slot ( ) , bank . slot ( ) ) ;
num_entries + = 1 ;
}
assert_eq! ( num_entries , 3 ) ;
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-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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2018-09-26 05:52:13 -07:00
2019-03-29 20:00:36 -07:00
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . last ( ) . unwrap ( ) . 1 , 4 ) ;
2019-05-18 14:01:36 -07:00
assert_eq! ( poh_recorder . tick_height , 4 ) ;
2019-03-29 20:00:36 -07:00
2021-03-15 17:11:15 -07:00
let start = Arc ::new ( Instant ::now ( ) ) ;
2019-03-29 20:00:36 -07:00
let working_bank = WorkingBank {
bank ,
2021-03-15 17:11:15 -07:00
start ,
2019-03-29 20:00:36 -07:00
min_tick_height : 2 ,
max_tick_height : 3 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
2018-12-12 18:52:11 -08:00
2019-05-18 14:01:36 -07:00
assert_eq! ( poh_recorder . tick_height , 5 ) ;
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 ;
}
assert_eq! ( num_entries , 3 ) ;
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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-19 16:17:36 -08:00
2021-03-15 17:11:15 -07:00
let start = Arc ::new ( Instant ::now ( ) ) ;
2019-03-29 20:00:36 -07:00
let working_bank = WorkingBank {
bank : bank . clone ( ) ,
2021-03-15 17:11:15 -07:00
start ,
2019-03-29 20:00:36 -07:00
min_tick_height : 2 ,
max_tick_height : 3 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
2020-05-15 09:35:43 -07:00
assert! ( poh_recorder . record ( bank . slot ( ) , h1 , vec! [ tx ] ) . is_err ( ) ) ;
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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-03-22 14:17:39 -07:00
2021-03-15 17:11:15 -07:00
let start = Arc ::new ( Instant ::now ( ) ) ;
2019-03-29 20:00:36 -07:00
let working_bank = WorkingBank {
bank : bank . clone ( ) ,
2021-03-15 17:11:15 -07:00
start ,
2019-03-29 20:00:36 -07:00
min_tick_height : 1 ,
max_tick_height : 2 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 1 ) ;
2019-05-18 14:01:36 -07:00
assert_eq! ( poh_recorder . tick_height , 1 ) ;
2019-03-29 20:00:36 -07:00
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
assert_matches! (
2020-05-15 09:35:43 -07:00
poh_recorder . record ( bank . slot ( ) + 1 , h1 , vec! [ tx ] ) ,
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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-19 16:17:36 -08:00
2021-03-15 17:11:15 -07:00
let start = Arc ::new ( Instant ::now ( ) ) ;
2019-03-29 20:00:36 -07:00
let working_bank = WorkingBank {
bank : bank . clone ( ) ,
2021-03-15 17:11:15 -07:00
start ,
2019-03-29 20:00:36 -07:00
min_tick_height : 1 ,
max_tick_height : 2 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 1 ) ;
2019-05-18 14:01:36 -07:00
assert_eq! ( poh_recorder . tick_height , 1 ) ;
2019-03-29 20:00:36 -07:00
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
2020-05-15 09:35:43 -07:00
assert! ( poh_recorder . record ( bank . slot ( ) , h1 , vec! [ tx ] ) . is_ok ( ) ) ;
2019-03-29 20:00:36 -07:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
//tick in the cache + entry
2019-09-18 12:16:22 -07:00
let ( _bank , ( e , _tick_height ) ) = entry_receiver . recv ( ) . expect ( " recv 1 " ) ;
assert! ( e . is_tick ( ) ) ;
let ( _bank , ( e , _tick_height ) ) = entry_receiver . recv ( ) . expect ( " recv 2 " ) ;
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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-19 16:17:36 -08:00
2021-03-15 17:11:15 -07:00
let start = Arc ::new ( Instant ::now ( ) ) ;
2019-03-29 20:00:36 -07:00
let working_bank = WorkingBank {
bank : bank . clone ( ) ,
2021-03-15 17:11:15 -07:00
start ,
2019-03-29 20:00:36 -07:00
min_tick_height : 1 ,
max_tick_height : 2 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
2019-05-18 14:01:36 -07:00
assert_eq! ( poh_recorder . tick_height , 2 ) ;
2019-03-29 20:00:36 -07:00
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
2020-05-15 09:35:43 -07:00
assert! ( poh_recorder . record ( bank . slot ( ) , h1 , vec! [ tx ] ) . is_err ( ) ) ;
2019-03-29 20:00:36 -07:00
2019-09-18 12:16:22 -07:00
let ( _bank , ( entry , _tick_height ) ) = entry_receiver . recv ( ) . unwrap ( ) ;
assert! ( entry . is_tick ( ) ) ;
let ( _bank , ( entry , _tick_height ) ) = entry_receiver . recv ( ) . unwrap ( ) ;
assert! ( entry . is_tick ( ) ) ;
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
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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-02-19 16:17:36 -08:00
2021-03-15 17:11:15 -07:00
let start = Arc ::new ( Instant ::now ( ) ) ;
2019-03-29 20:00:36 -07:00
let working_bank = WorkingBank {
bank ,
2021-03-15 17:11:15 -07:00
start ,
2019-03-29 20:00:36 -07:00
min_tick_height : 2 ,
max_tick_height : 3 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
2019-05-18 14:01:36 -07:00
assert_eq! ( poh_recorder . tick_height , 2 ) ;
2019-03-29 20:00:36 -07:00
drop ( entry_receiver ) ;
poh_recorder . tick ( ) ;
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 3 ) ;
}
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-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 ( ) ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 2 ) ;
2019-05-18 14:01:36 -07:00
let hash = poh_recorder . poh . lock ( ) . unwrap ( ) . hash ;
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( hash , 0 , 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-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 ( ) ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 2 ) ;
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( poh_recorder . tick_cache [ 0 ] . 0. hash , 0 , 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-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 ( ) ,
0 ,
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 ( ) ) ,
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 ) ;
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( hash ( b " hello " ) , 0 , 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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ( ) ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2021-03-15 17:11:15 -07:00
let start = Arc ::new ( Instant ::now ( ) ) ;
2019-03-29 20:00:36 -07:00
let working_bank = WorkingBank {
bank ,
2021-03-15 17:11:15 -07:00
start ,
2019-03-29 20:00:36 -07:00
min_tick_height : 2 ,
max_tick_height : 3 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( hash ( b " hello " ) , 0 , 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 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_config ) ) ;
2019-04-09 17:53:04 -07:00
let ( sender , receiver ) = sync_channel ( 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 ( ) ,
0 ,
None ,
bank . ticks_per_slot ( ) ,
& Pubkey ::default ( ) ,
& Arc ::new ( blockstore ) ,
Some ( sender ) ,
& Arc ::new ( LeaderScheduleCache ::default ( ) ) ,
& Arc ::new ( PohConfig ::default ( ) ) ,
) ;
2019-03-29 20:00:36 -07:00
poh_recorder . set_bank ( & bank ) ;
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 ]
fn test_poh_recorder_reset_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 ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
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 ( ) ) ,
2019-03-29 20:00:36 -07:00
) ;
2019-03-13 14:06:12 -07:00
2019-03-29 20:00:36 -07:00
let end_slot = 3 ;
2019-10-16 12:53:11 -07:00
let max_tick_height = ( end_slot + 1 ) * ticks_per_slot ;
2021-03-15 17:11:15 -07:00
let start = Arc ::new ( Instant ::now ( ) ) ;
2019-03-29 20:00:36 -07:00
let working_bank = WorkingBank {
bank : bank . clone ( ) ,
2021-03-15 17:11:15 -07:00
start ,
2019-03-29 20:00:36 -07:00
min_tick_height : 1 ,
max_tick_height ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
for _ in 0 .. max_tick_height {
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! " ) ;
2020-05-15 09:35:43 -07:00
assert! ( poh_recorder . record ( bank . slot ( ) , h1 , vec! [ tx ] ) . is_err ( ) ) ;
2019-03-29 20:00:36 -07:00
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
// Make sure the starting slot is updated
2019-07-17 14:10:15 -07:00
assert_eq! ( poh_recorder . start_slot , end_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 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_config ) ) ;
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 ,
0 ,
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 ( ) ) ,
) ;
2020-02-29 00:05:35 -08:00
let bootstrap_validator_id = leader_schedule_cache . slot_leader_at ( 0 , None ) . unwrap ( ) ;
2020-01-13 15:55:41 -08:00
assert_eq! ( poh_recorder . reached_leader_tick ( 0 ) , true ) ;
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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
None ,
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 ( ) ) ,
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
assert_eq! ( poh_recorder . reached_leader_slot ( ) . 0 , false ) ;
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
2019-07-17 14:10:15 -07:00
poh_recorder . reset ( bank . last_blockhash ( ) , 0 , None ) ;
2019-10-16 12:53:11 -07:00
assert_eq! ( poh_recorder . reached_leader_slot ( ) . 0 , false ) ;
2019-03-15 13:22:16 -07:00
2019-10-16 12:53:11 -07:00
// Provide a leader slot one slot down
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( bank . last_blockhash ( ) , 0 , 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
for _ in 0 .. bank . ticks_per_slot ( ) {
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 ( ) ,
init_ticks + bank . ticks_per_slot ( )
) ;
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
assert_eq! ( poh_recorder . reached_leader_slot ( ) . 0 , false ) ;
2019-03-15 13:22:16 -07:00
2019-07-17 14:10:15 -07:00
// reset poh now. we should immediately be leader
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( bank . last_blockhash ( ) , 1 , Some ( ( 2 , 2 ) ) ) ;
2019-10-16 12:53:11 -07:00
let ( reached_leader_slot , grace_ticks , leader_slot , .. ) =
poh_recorder . reached_leader_slot ( ) ;
assert_eq! ( reached_leader_slot , true ) ;
assert_eq! ( grace_ticks , 0 ) ;
assert_eq! ( leader_slot , 2 ) ;
// Now test that with grace ticks we can reach leader slot
// Set the leader slot one slot down
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( bank . last_blockhash ( ) , 1 , 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)
2019-03-29 20:00:36 -07:00
for _ in 0 .. bank . ticks_per_slot ( ) {
poh_recorder . tick ( ) ;
}
// We are not the leader yet, as expected
2019-10-16 12:53:11 -07:00
assert_eq! ( poh_recorder . reached_leader_slot ( ) . 0 , false ) ;
2019-03-15 13:22:16 -07:00
2019-10-16 12:53:11 -07:00
// Send the grace ticks
for _ in 0 .. bank . 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
2019-10-16 12:53:11 -07:00
let ( reached_leader_slot , grace_ticks , leader_slot , .. ) =
poh_recorder . reached_leader_slot ( ) ;
assert_eq! ( reached_leader_slot , true ) ;
assert_eq! ( grace_ticks , bank . ticks_per_slot ( ) / GRACE_TICKS_FACTOR ) ;
assert_eq! ( leader_slot , 3 ) ;
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
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( bank . last_blockhash ( ) , 2 , Some ( ( 4 , 4 ) ) ) ;
2019-03-18 13:24:07 -07:00
2019-07-17 14:10:15 -07:00
// send ticks for a slot
for _ in 0 .. bank . 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
2019-10-16 12:53:11 -07:00
assert_eq! ( poh_recorder . reached_leader_slot ( ) . 0 , false ) ;
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( bank . last_blockhash ( ) , 3 , 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
2019-10-16 12:53:11 -07:00
let ( reached_leader_slot , grace_ticks , leader_slot , .. ) =
poh_recorder . reached_leader_slot ( ) ;
assert_eq! ( reached_leader_slot , true ) ;
assert_eq! ( grace_ticks , 0 ) ;
assert_eq! ( leader_slot , 4 ) ;
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
2019-07-26 11:33:51 -07:00
poh_recorder . reset ( bank . last_blockhash ( ) , 4 , 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 ;
for _ in 0 .. overshoot_factor * bank . 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
2019-10-16 12:53:11 -07:00
let ( reached_leader_slot , grace_ticks , leader_slot , .. ) =
poh_recorder . reached_leader_slot ( ) ;
assert_eq! ( reached_leader_slot , true ) ;
assert_eq! ( grace_ticks , overshoot_factor * bank . ticks_per_slot ( ) ) ;
assert_eq! ( leader_slot , 9 ) ;
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 ) ;
let bank = Arc ::new ( Bank ::new ( & 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 ,
0 ,
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 ( ) ) ,
2019-04-22 19:49:32 -07:00
) ;
// Test that with no leader slot, we don't reach the leader tick
assert_eq! (
2019-04-23 11:56:30 -07:00
poh_recorder . would_be_leader ( 2 * bank . ticks_per_slot ( ) ) ,
2019-04-22 19:49:32 -07:00
false
) ;
2019-07-17 14:10:15 -07:00
poh_recorder . reset ( bank . last_blockhash ( ) , 0 , None ) ;
2019-04-22 19:49:32 -07:00
assert_eq! (
2019-04-23 11:56:30 -07:00
poh_recorder . would_be_leader ( 2 * bank . ticks_per_slot ( ) ) ,
2019-04-22 19:49:32 -07:00
false
) ;
// We reset with leader slot after 3 slots
2019-07-26 11:33:51 -07:00
let bank_slot = bank . slot ( ) + 3 ;
poh_recorder . reset ( bank . last_blockhash ( ) , 0 , 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
assert_eq! (
2019-04-23 11:56:30 -07:00
poh_recorder . would_be_leader ( 2 * bank . ticks_per_slot ( ) ) ,
2019-04-22 19:49:32 -07:00
false
) ;
// Test that the node will be leader in next 3 slots
2019-04-23 11:56:30 -07:00
assert_eq! (
poh_recorder . would_be_leader ( 3 * bank . ticks_per_slot ( ) ) ,
true
) ;
2019-04-22 19:49:32 -07:00
assert_eq! (
2019-04-23 11:56:30 -07:00
poh_recorder . would_be_leader ( 2 * bank . ticks_per_slot ( ) ) ,
2019-04-22 19:49:32 -07:00
false
) ;
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
poh_recorder . set_bank ( & bank ) ;
2019-04-23 11:56:30 -07:00
assert_eq! (
poh_recorder . would_be_leader ( 2 * bank . ticks_per_slot ( ) ) ,
true
) ;
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 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_config ) ) ;
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 ( ) ,
0 ,
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 ( ) ) ,
) ;
//create a new bank
let bank = Arc ::new ( Bank ::new_from_parent ( & bank , & Pubkey ::default ( ) , 2 ) ) ;
//put 2 slots worth of virtual ticks into poh
for _ in 0 .. ( bank . ticks_per_slot ( ) * 2 ) {
poh_recorder . tick ( ) ;
}
2020-06-08 17:38:14 -07:00
poh_recorder . set_bank ( & bank ) ;
2020-01-16 23:26:50 -08:00
assert_eq! ( Some ( false ) , bank . check_hash_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
}