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:
//! * tick must be > WorkingBank::min_tick_height && tick must be <= WorkingBank::man_tick_height
//!
//! For Entries:
//! * recorded entry must be >= WorkingBank::min_tick_height && entry must be < WorkingBank::man_tick_height
//!
2018-12-07 19:16:27 -08:00
use crate ::entry ::Entry ;
2019-03-20 13:49:46 -07:00
use crate ::leader_schedule_utils ;
2018-12-07 19:16:27 -08:00
use crate ::poh ::Poh ;
use crate ::result ::{ Error , Result } ;
2019-02-18 22:26:22 -08:00
use solana_runtime ::bank ::Bank ;
2018-11-16 08:04:46 -08:00
use solana_sdk ::hash ::Hash ;
2019-03-20 13:49:46 -07:00
use solana_sdk ::pubkey ::Pubkey ;
2018-11-29 16:18:47 -08:00
use solana_sdk ::transaction ::Transaction ;
2019-03-11 13:58:23 -07:00
use std ::sync ::mpsc ::{ channel , Receiver , Sender , SyncSender } ;
2019-02-24 08:59:49 -08:00
use std ::sync ::Arc ;
2018-09-26 05:52:13 -07:00
2019-03-15 13:22:16 -07:00
const MAX_LAST_LEADER_GRACE_TICKS_FACTOR : u64 = 2 ;
2018-10-18 22:57:48 -07:00
#[ derive(Debug, PartialEq, Eq, Clone) ]
pub enum PohRecorderError {
2018-10-25 14:56:21 -07:00
InvalidCallingObject ,
2018-10-18 22:57:48 -07:00
MaxHeightReached ,
2019-02-19 16:17:36 -08:00
MinHeightNotReached ,
}
2019-03-03 16:44:06 -08:00
pub type WorkingBankEntries = ( Arc < Bank > , Vec < ( Entry , u64 ) > ) ;
2019-02-19 16:17:36 -08:00
#[ derive(Clone) ]
pub struct WorkingBank {
pub bank : Arc < Bank > ,
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-02-26 10:48:18 -08:00
pub poh : Poh ,
2019-03-12 17:42:53 -07:00
pub clear_bank_signal : Option < SyncSender < bool > > ,
start_slot : u64 ,
2019-03-15 13:22:16 -07:00
start_tick : u64 ,
2019-02-24 08:59:49 -08:00
tick_cache : Vec < ( Entry , u64 ) > ,
working_bank : Option < WorkingBank > ,
2019-03-03 16:44:06 -08:00
sender : Sender < WorkingBankEntries > ,
2019-03-15 13:22:16 -07:00
start_leader_at_tick : Option < u64 > ,
2019-03-20 15:14:21 -07:00
last_leader_tick : Option < u64 > ,
2019-03-15 13:22:16 -07:00
max_last_leader_grace_ticks : u64 ,
2019-03-20 13:49:46 -07:00
id : Pubkey ,
2018-09-26 05:52:13 -07:00
}
impl PohRecorder {
2019-02-24 08:59:49 -08:00
pub 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-03-20 13:49:46 -07:00
let next_leader_slot =
2019-03-20 14:50:02 -07:00
leader_schedule_utils ::next_leader_slot ( & self . id , bank . slot ( ) , & bank ) ;
2019-03-20 15:14:21 -07:00
let ( start_leader_at_tick , last_leader_tick ) = Self ::compute_leader_slot_ticks (
& next_leader_slot ,
bank . ticks_per_slot ( ) ,
self . max_last_leader_grace_ticks ,
) ;
self . start_leader_at_tick = start_leader_at_tick ;
self . last_leader_tick = last_leader_tick ;
2019-03-20 13:49:46 -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
}
pub fn hash ( & mut self ) {
2018-09-26 05:52:13 -07:00
// TODO: amortize the cost of this lock by doing the loop in here for
// some min amount of hashes
2019-02-24 08:59:49 -08:00
self . poh . hash ( ) ;
2019-02-19 16:17:36 -08:00
}
2018-12-12 18:52:11 -08:00
2019-03-12 17:42:53 -07:00
pub fn start_slot ( & self ) -> u64 {
self . start_slot
}
2019-03-03 16:44:06 -08:00
pub fn bank ( & self ) -> Option < Arc < Bank > > {
self . working_bank . clone ( ) . map ( | w | w . bank )
}
2019-03-12 17:42:53 -07:00
2019-03-04 07:08:22 -08:00
pub fn tick_height ( & self ) -> u64 {
self . poh . tick_height
}
2019-03-12 17:42:53 -07:00
2019-03-18 13:24:07 -07:00
// returns if leader tick has reached, and how many grace ticks were afforded
pub fn reached_leader_tick ( & self ) -> ( bool , u64 ) {
2019-03-15 13:22:16 -07:00
self . start_leader_at_tick
. map ( | target_tick | {
2019-03-15 15:17:19 -07:00
debug! (
2019-03-15 13:22:16 -07:00
" Current tick {}, start tick {} target {}, grace {} " ,
self . tick_height ( ) ,
self . start_tick ,
target_tick ,
self . max_last_leader_grace_ticks
) ;
2019-03-18 13:24:07 -07:00
let leader_ideal_start_tick =
target_tick . saturating_sub ( self . max_last_leader_grace_ticks ) ;
2019-03-20 13:49:46 -07:00
// Is the current tick in the same slot as the target tick?
// Check if either grace period has expired,
// or target tick is = grace period (i.e. poh recorder was just reset)
2019-03-20 15:14:21 -07:00
if self . tick_height ( ) < = self . last_leader_tick . unwrap_or ( 0 )
2019-03-19 14:49:36 -07:00
& & ( self . tick_height ( ) > = target_tick
| | self . max_last_leader_grace_ticks
> = target_tick . saturating_sub ( self . start_tick ) )
2019-03-18 13:24:07 -07:00
{
return (
true ,
self . tick_height ( ) . saturating_sub ( leader_ideal_start_tick ) ,
) ;
}
( false , 0 )
2019-03-15 13:22:16 -07:00
} )
2019-03-18 13:24:07 -07:00
. unwrap_or ( ( false , 0 ) )
2019-03-15 13:22:16 -07:00
}
2019-03-20 15:14:21 -07:00
fn compute_leader_slot_ticks (
next_leader_slot : & Option < u64 > ,
ticks_per_slot : u64 ,
grace_ticks : u64 ,
) -> ( Option < u64 > , Option < u64 > ) {
next_leader_slot
. map ( | slot | {
(
Some ( slot * ticks_per_slot + grace_ticks ) ,
Some ( ( slot + 1 ) * ticks_per_slot - 1 ) ,
)
} )
. unwrap_or ( ( None , None ) )
}
2019-02-24 08:59:49 -08:00
// synchronize PoH with a bank
2019-03-15 13:22:16 -07:00
pub fn reset (
& mut self ,
tick_height : u64 ,
blockhash : Hash ,
start_slot : u64 ,
my_next_leader_slot : Option < u64 > ,
ticks_per_slot : u64 ,
) {
2019-03-05 17:56:51 -08:00
self . clear_bank ( ) ;
2019-02-19 16:17:36 -08:00
let mut cache = vec! [ ] ;
2019-02-24 08:59:49 -08:00
info! (
" reset poh from: {},{} to: {},{} " ,
2019-03-02 10:25:16 -08:00
self . poh . hash , self . poh . tick_height , blockhash , tick_height ,
2019-02-24 08:59:49 -08:00
) ;
std ::mem ::swap ( & mut cache , & mut self . tick_cache ) ;
2019-03-12 17:42:53 -07:00
self . start_slot = start_slot ;
2019-03-15 13:22:16 -07:00
self . start_tick = tick_height + 1 ;
2019-03-02 10:25:16 -08:00
self . poh = Poh ::new ( blockhash , tick_height ) ;
2019-03-15 13:22:16 -07:00
self . max_last_leader_grace_ticks = ticks_per_slot / MAX_LAST_LEADER_GRACE_TICKS_FACTOR ;
2019-03-20 15:14:21 -07:00
let ( start_leader_at_tick , last_leader_tick ) = Self ::compute_leader_slot_ticks (
& my_next_leader_slot ,
ticks_per_slot ,
self . max_last_leader_grace_ticks ,
) ;
self . start_leader_at_tick = start_leader_at_tick ;
self . last_leader_tick = last_leader_tick ;
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-02-24 08:59:49 -08:00
self . working_bank = Some ( working_bank ) ;
}
2019-03-03 16:44:06 -08:00
pub fn set_bank ( & mut self , bank : & Arc < Bank > ) {
let max_tick_height = ( bank . slot ( ) + 1 ) * bank . ticks_per_slot ( ) - 1 ;
let working_bank = WorkingBank {
bank : bank . clone ( ) ,
min_tick_height : bank . tick_height ( ) ,
max_tick_height ,
} ;
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 ( )
. ok_or ( Error ::PohRecorderError ( PohRecorderError ::MaxHeightReached ) ) ? ;
if self . poh . tick_height < working_bank . min_tick_height {
return Err ( Error ::PohRecorderError (
PohRecorderError ::MinHeightNotReached ,
) ) ;
}
if tick & & self . poh . tick_height = = working_bank . min_tick_height {
return Err ( Error ::PohRecorderError (
PohRecorderError ::MinHeightNotReached ,
) ) ;
}
let cnt = self
. tick_cache
. iter ( )
. take_while ( | x | x . 1 < = working_bank . max_tick_height )
. count ( ) ;
let e = if cnt > 0 {
2019-02-26 21:57:45 -08:00
debug! (
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 ,
cnt ,
) ;
2019-02-25 13:50:31 -08:00
let cache = & self . tick_cache [ .. cnt ] ;
for t in cache {
2019-03-01 08:57:42 -08:00
working_bank . bank . register_tick ( & t . 0. hash ) ;
2019-02-19 16:17:36 -08:00
}
2019-03-03 16:44:06 -08:00
self . sender
. send ( ( working_bank . bank . clone ( ) , cache . to_vec ( ) ) )
2019-02-24 08:59:49 -08:00
} else {
Ok ( ( ) )
} ;
if self . poh . tick_height > = working_bank . max_tick_height {
2019-03-03 16:44:06 -08:00
info! (
" poh_record: max_tick_height reached, setting working bank {} to None " ,
working_bank . bank . slot ( )
) ;
2019-03-13 14:06:12 -07:00
self . start_slot = working_bank . max_tick_height / working_bank . bank . ticks_per_slot ( ) ;
2019-03-11 13:58:23 -07:00
self . clear_bank ( ) ;
2019-02-19 16:17:36 -08:00
}
2019-02-24 08:59:49 -08:00
if e . is_err ( ) {
info! ( " WorkingBank::sender disconnected {:?} " , e ) ;
//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 {
//commit the flush
let _ = self . tick_cache . drain ( .. cnt ) ;
}
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-03-15 13:22:16 -07:00
if self . start_leader_at_tick . is_none ( ) {
return ;
}
2019-02-24 08:59:49 -08:00
let tick = self . generate_tick ( ) ;
trace! ( " tick {} " , tick . 1 ) ;
self . tick_cache . push ( tick ) ;
let _ = self . flush_cache ( true ) ;
2018-09-26 05:52:13 -07:00
}
2019-02-24 08:59:49 -08:00
pub fn record ( & mut self , mixin : Hash , txs : Vec < Transaction > ) -> Result < ( ) > {
self . flush_cache ( false ) ? ;
self . record_and_send_txs ( mixin , txs )
2018-10-18 22:57:48 -07:00
}
2019-03-03 16:44:06 -08: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
2019-03-12 17:42:53 -07:00
pub fn new (
tick_height : u64 ,
last_entry_hash : Hash ,
start_slot : u64 ,
2019-03-15 13:22:16 -07:00
my_leader_slot_index : Option < u64 > ,
ticks_per_slot : u64 ,
2019-03-20 14:23:58 -07:00
id : & Pubkey ,
2019-03-12 17:42:53 -07:00
) -> ( Self , Receiver < WorkingBankEntries > ) {
2019-03-01 08:26:47 -08:00
let poh = Poh ::new ( last_entry_hash , tick_height ) ;
2019-03-03 16:44:06 -08:00
let ( sender , receiver ) = channel ( ) ;
2019-03-20 15:14:21 -07:00
let max_last_leader_grace_ticks = ticks_per_slot / MAX_LAST_LEADER_GRACE_TICKS_FACTOR ;
let ( start_leader_at_tick , last_leader_tick ) = Self ::compute_leader_slot_ticks (
& my_leader_slot_index ,
ticks_per_slot ,
max_last_leader_grace_ticks ,
) ;
2019-03-03 16:44:06 -08:00
(
PohRecorder {
poh ,
tick_cache : vec ! [ ] ,
working_bank : None ,
sender ,
2019-03-11 13:58:23 -07:00
clear_bank_signal : None ,
2019-03-12 17:42:53 -07:00
start_slot ,
2019-03-15 13:22:16 -07:00
start_tick : tick_height + 1 ,
2019-03-20 15:14:21 -07:00
start_leader_at_tick ,
last_leader_tick ,
max_last_leader_grace_ticks ,
2019-03-20 14:23:58 -07:00
id : * id ,
2019-03-03 16:44:06 -08:00
} ,
receiver ,
)
2018-10-18 22:57:48 -07:00
}
2019-02-24 08:59:49 -08:00
fn record_and_send_txs ( & mut self , mixin : Hash , txs : Vec < Transaction > ) -> Result < ( ) > {
let working_bank = self
. working_bank
. as_ref ( )
. ok_or ( Error ::PohRecorderError ( PohRecorderError ::MaxHeightReached ) ) ? ;
2019-03-01 09:21:58 -08:00
let poh_entry = self . poh . record ( mixin ) ;
2018-10-10 17:23:06 -07:00
assert! ( ! txs . is_empty ( ) , " Entries without transactions are used to track real-time passing in the ledger and can only be generated with PohRecorder::tick function " ) ;
2019-02-25 13:50:31 -08:00
let recorded_entry = Entry {
2019-03-01 09:21:58 -08:00
num_hashes : poh_entry . num_hashes ,
hash : poh_entry . hash ,
2018-09-26 05:52:13 -07:00
transactions : txs ,
} ;
2019-02-25 13:50:31 -08:00
trace! ( " sending entry {} " , recorded_entry . is_tick ( ) ) ;
2019-03-03 16:44:06 -08:00
self . sender . send ( (
working_bank . bank . clone ( ) ,
vec! [ ( recorded_entry , poh_entry . tick_height ) ] ,
) ) ? ;
2018-09-26 05:52:13 -07:00
Ok ( ( ) )
}
2018-10-18 22:57:48 -07:00
2019-02-24 08:59:49 -08:00
fn generate_tick ( & mut self ) -> ( Entry , u64 ) {
let tick = self . poh . tick ( ) ;
assert_ne! ( tick . tick_height , 0 ) ;
(
Entry {
num_hashes : tick . num_hashes ,
2019-03-01 09:21:58 -08:00
hash : tick . hash ,
2019-02-24 08:59:49 -08:00
transactions : vec ! [ ] ,
} ,
tick . tick_height ,
)
2018-10-18 22:57:48 -07:00
}
2018-09-26 05:52:13 -07:00
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2018-12-07 19:16:27 -08:00
use crate ::test_tx ::test_tx ;
2019-02-18 22:26:22 -08:00
use solana_sdk ::genesis_block ::GenesisBlock ;
2018-11-16 08:04:46 -08:00
use solana_sdk ::hash ::hash ;
2019-03-15 13:22:16 -07:00
use solana_sdk ::timing ::DEFAULT_TICKS_PER_SLOT ;
2019-03-11 13:58:23 -07:00
use std ::sync ::mpsc ::sync_channel ;
2018-09-26 05:52:13 -07:00
use std ::sync ::Arc ;
#[ 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-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
Some ( 4 ) ,
DEFAULT_TICKS_PER_SLOT ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-02-24 08:59:49 -08:00
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 1 ) ;
assert_eq! ( poh_recorder . tick_cache [ 0 ] . 1 , 1 ) ;
assert_eq! ( poh_recorder . poh . tick_height , 1 ) ;
}
#[ 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-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
Some ( 4 ) ,
DEFAULT_TICKS_PER_SLOT ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-02-24 08:59:49 -08:00
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 2 ) ;
assert_eq! ( poh_recorder . tick_cache [ 1 ] . 1 , 2 ) ;
assert_eq! ( poh_recorder . poh . tick_height , 2 ) ;
}
#[ test ]
fn test_poh_recorder_reset_clears_cache ( ) {
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
Hash ::default ( ) ,
0 ,
Some ( 4 ) ,
DEFAULT_TICKS_PER_SLOT ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-02-24 08:59:49 -08:00
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 1 ) ;
2019-03-15 13:22:16 -07:00
poh_recorder . reset ( 0 , Hash ::default ( ) , 0 , Some ( 4 ) , DEFAULT_TICKS_PER_SLOT ) ;
2019-02-24 08:59:49 -08:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
}
#[ test ]
fn test_poh_recorder_clear ( ) {
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
2019-03-02 10:25:16 -08:00
let prev_hash = bank . last_blockhash ( ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-02-24 08:59:49 -08:00
let working_bank = WorkingBank {
bank ,
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 ( ) ) ;
}
#[ test ]
fn test_poh_recorder_tick_sent_after_min ( ) {
2019-02-05 08:03:52 -08:00
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
2019-01-24 12:04:04 -08:00
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
2019-03-02 10:25:16 -08:00
let prev_hash = bank . last_blockhash ( ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-02-19 16:17:36 -08:00
let working_bank = WorkingBank {
2019-03-03 16:44:06 -08:00
bank : bank . clone ( ) ,
2019-02-24 08:59:49 -08:00
min_tick_height : 2 ,
max_tick_height : 3 ,
2019-02-19 16:17:36 -08:00
} ;
2019-02-24 08:59:49 -08:00
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 ( ) ) ;
2018-09-26 05:52:13 -07:00
2019-02-24 08:59:49 -08:00
// all ticks are sent after height > min
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . poh . tick_height , 3 ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
2019-03-03 16:44:06 -08:00
let ( bank_ , e ) = entry_receiver . recv ( ) . expect ( " recv 1 " ) ;
2019-02-24 08:59:49 -08:00
assert_eq! ( e . len ( ) , 3 ) ;
2019-03-03 16:44:06 -08:00
assert_eq! ( bank_ . slot ( ) , bank . slot ( ) ) ;
2019-02-24 08:59:49 -08:00
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
}
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 ( ) {
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
2019-03-02 10:25:16 -08:00
let prev_hash = bank . last_blockhash ( ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2018-09-26 05:52:13 -07:00
2019-02-24 08:59:49 -08:00
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . last ( ) . unwrap ( ) . 1 , 4 ) ;
assert_eq! ( poh_recorder . poh . tick_height , 4 ) ;
2018-09-26 05:52:13 -07:00
2019-02-24 08:59:49 -08:00
let working_bank = WorkingBank {
bank ,
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-02-24 08:59:49 -08:00
assert_eq! ( poh_recorder . poh . tick_height , 5 ) ;
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
2019-03-03 16:44:06 -08:00
let ( _ , e ) = entry_receiver . recv ( ) . expect ( " recv 1 " ) ;
2019-02-24 08:59:49 -08:00
assert_eq! ( e . len ( ) , 3 ) ;
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-02-19 16:17:36 -08:00
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
2019-03-02 10:25:16 -08:00
let prev_hash = bank . last_blockhash ( ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-02-19 16:17:36 -08:00
let working_bank = WorkingBank {
bank ,
2019-02-24 08:59:49 -08:00
min_tick_height : 2 ,
max_tick_height : 3 ,
2019-02-19 16:17:36 -08:00
} ;
2019-02-24 08:59:49 -08:00
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
assert! ( poh_recorder . record ( h1 , vec! [ tx . clone ( ) ] ) . is_err ( ) ) ;
2019-02-19 16:17:36 -08:00
assert! ( entry_receiver . try_recv ( ) . is_err ( ) ) ;
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_recorder_record_at_min_passes ( ) {
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
2019-03-02 10:25:16 -08:00
let prev_hash = bank . last_blockhash ( ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-02-19 16:17:36 -08:00
2019-02-24 08:59:49 -08:00
let working_bank = WorkingBank {
bank ,
min_tick_height : 1 ,
max_tick_height : 2 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 1 ) ;
assert_eq! ( poh_recorder . poh . tick_height , 1 ) ;
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
assert! ( poh_recorder . record ( h1 , vec! [ tx . clone ( ) ] ) . is_ok ( ) ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
//tick in the cache + entry
2019-03-03 16:44:06 -08:00
let ( _b , e ) = entry_receiver . recv ( ) . expect ( " recv 1 " ) ;
2019-02-24 08:59:49 -08:00
assert_eq! ( e . len ( ) , 1 ) ;
2019-02-25 13:50:31 -08:00
assert! ( e [ 0 ] . 0. is_tick ( ) ) ;
2019-03-03 16:44:06 -08:00
let ( _b , e ) = entry_receiver . recv ( ) . expect ( " recv 2 " ) ;
2019-02-25 13:50:31 -08:00
assert! ( ! e [ 0 ] . 0. is_tick ( ) ) ;
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-02-19 16:17:36 -08:00
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
2019-03-02 10:25:16 -08:00
let prev_hash = bank . last_blockhash ( ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-02-19 16:17:36 -08:00
let working_bank = WorkingBank {
bank ,
min_tick_height : 1 ,
2019-02-24 08:59:49 -08:00
max_tick_height : 2 ,
2019-02-19 16:17:36 -08:00
} ;
2019-02-24 08:59:49 -08:00
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . poh . tick_height , 2 ) ;
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
assert! ( poh_recorder . record ( h1 , vec! [ tx . clone ( ) ] ) . is_err ( ) ) ;
2019-02-19 16:17:36 -08:00
2019-03-03 16:44:06 -08:00
let ( _bank , e ) = entry_receiver . recv ( ) . expect ( " recv 1 " ) ;
2019-02-24 08:59:49 -08:00
assert_eq! ( e . len ( ) , 2 ) ;
2019-02-25 13:50:31 -08:00
assert! ( e [ 0 ] . 0. is_tick ( ) ) ;
assert! ( e [ 1 ] . 0. is_tick ( ) ) ;
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 ( ) {
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
2019-03-02 10:25:16 -08:00
let prev_hash = bank . last_blockhash ( ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-02-19 16:17:36 -08:00
2019-02-24 08:59:49 -08:00
let working_bank = WorkingBank {
bank ,
min_tick_height : 2 ,
max_tick_height : 3 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . poh . tick_height , 2 ) ;
drop ( entry_receiver ) ;
poh_recorder . tick ( ) ;
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 3 ) ;
2018-09-26 05:52:13 -07:00
}
2019-03-04 07:08:22 -08:00
#[ test ]
fn test_reset_current ( ) {
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
Hash ::default ( ) ,
0 ,
Some ( 4 ) ,
DEFAULT_TICKS_PER_SLOT ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-03-04 07:08:22 -08:00
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 2 ) ;
2019-03-15 13:22:16 -07:00
poh_recorder . reset (
poh_recorder . poh . tick_height ,
poh_recorder . poh . hash ,
0 ,
Some ( 4 ) ,
DEFAULT_TICKS_PER_SLOT ,
) ;
2019-03-13 14:06:12 -07:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
2019-03-04 07:08:22 -08:00
}
#[ test ]
fn test_reset_with_cached ( ) {
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
Hash ::default ( ) ,
0 ,
Some ( 4 ) ,
DEFAULT_TICKS_PER_SLOT ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-03-04 07:08:22 -08:00
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 2 ) ;
poh_recorder . reset (
poh_recorder . tick_cache [ 0 ] . 1 ,
poh_recorder . tick_cache [ 0 ] . 0. hash ,
2019-03-12 17:42:53 -07:00
0 ,
2019-03-15 13:22:16 -07:00
Some ( 4 ) ,
DEFAULT_TICKS_PER_SLOT ,
2019-03-04 07:08:22 -08:00
) ;
2019-03-13 14:06:12 -07:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
2019-03-04 07:08:22 -08:00
}
#[ test ]
fn test_reset_to_new_value ( ) {
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
Hash ::default ( ) ,
0 ,
Some ( 4 ) ,
DEFAULT_TICKS_PER_SLOT ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-03-04 07:08:22 -08:00
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . tick_cache . len ( ) , 3 ) ;
assert_eq! ( poh_recorder . poh . tick_height , 3 ) ;
2019-03-15 13:22:16 -07:00
poh_recorder . reset ( 1 , hash ( b " hello " ) , 0 , Some ( 4 ) , DEFAULT_TICKS_PER_SLOT ) ;
2019-03-04 07:08:22 -08:00
assert_eq! ( poh_recorder . tick_cache . len ( ) , 0 ) ;
poh_recorder . tick ( ) ;
assert_eq! ( poh_recorder . poh . tick_height , 2 ) ;
}
2019-03-05 17:56:51 -08:00
#[ test ]
fn test_reset_clear_bank ( ) {
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
Hash ::default ( ) ,
0 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-03-15 13:22:16 -07:00
let ticks_per_slot = bank . ticks_per_slot ( ) ;
2019-03-05 17:56:51 -08:00
let working_bank = WorkingBank {
bank ,
min_tick_height : 2 ,
max_tick_height : 3 ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
2019-03-15 13:22:16 -07:00
poh_recorder . reset ( 1 , hash ( b " hello " ) , 0 , Some ( 4 ) , ticks_per_slot ) ;
2019-03-05 17:56:51 -08:00
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
}
2019-03-11 13:58:23 -07:00
#[ test ]
pub fn test_clear_signal ( ) {
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
Hash ::default ( ) ,
0 ,
None ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-03-11 13:58:23 -07:00
let ( sender , receiver ) = sync_channel ( 1 ) ;
poh_recorder . set_bank ( & bank ) ;
poh_recorder . clear_bank_signal = Some ( sender ) ;
poh_recorder . clear_bank ( ) ;
assert! ( receiver . try_recv ( ) . is_ok ( ) ) ;
}
2019-03-13 14:06:12 -07:00
#[ test ]
fn test_poh_recorder_reset_start_slot ( ) {
let ticks_per_slot = 5 ;
let ( mut genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
genesis_block . ticks_per_slot = ticks_per_slot ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
let prev_hash = bank . last_blockhash ( ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-03-13 14:06:12 -07:00
let end_slot = 3 ;
let max_tick_height = ( end_slot + 1 ) * ticks_per_slot - 1 ;
let working_bank = WorkingBank {
bank ,
min_tick_height : 1 ,
max_tick_height ,
} ;
poh_recorder . set_working_bank ( working_bank ) ;
for _ in 0 .. max_tick_height {
poh_recorder . tick ( ) ;
}
let tx = test_tx ( ) ;
let h1 = hash ( b " hello world! " ) ;
assert! ( poh_recorder . record ( h1 , vec! [ tx . clone ( ) ] ) . is_err ( ) ) ;
assert! ( poh_recorder . working_bank . is_none ( ) ) ;
// Make sure the starting slot is updated
assert_eq! ( poh_recorder . start_slot ( ) , end_slot ) ;
}
2019-03-15 13:22:16 -07:00
#[ test ]
fn test_reached_leader_tick ( ) {
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 2 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
let prev_hash = bank . last_blockhash ( ) ;
2019-03-20 13:49:46 -07:00
let ( mut poh_recorder , _entry_receiver ) = PohRecorder ::new (
0 ,
prev_hash ,
0 ,
None ,
bank . ticks_per_slot ( ) ,
2019-03-20 14:23:58 -07:00
& Pubkey ::default ( ) ,
2019-03-20 13:49:46 -07:00
) ;
2019-03-15 13:22:16 -07:00
// Test that with no leader slot, we don't reach the leader tick
2019-03-18 13:24:07 -07:00
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , false ) ;
2019-03-15 13:22:16 -07:00
for _ in 0 .. bank . ticks_per_slot ( ) {
poh_recorder . tick ( ) ;
}
// Tick should not be recorded
assert_eq! ( poh_recorder . tick_height ( ) , 0 ) ;
// Test that with no leader slot, we don't reach the leader tick after sending some ticks
2019-03-18 13:24:07 -07:00
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , false ) ;
2019-03-15 13:22:16 -07:00
poh_recorder . reset (
poh_recorder . tick_height ( ) ,
bank . last_blockhash ( ) ,
0 ,
None ,
bank . ticks_per_slot ( ) ,
) ;
// Test that with no leader slot in reset(), we don't reach the leader tick
2019-03-18 13:24:07 -07:00
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , false ) ;
2019-03-15 13:22:16 -07:00
// Provide a leader slot 1 slot down
poh_recorder . reset (
bank . ticks_per_slot ( ) ,
bank . last_blockhash ( ) ,
0 ,
Some ( 2 ) ,
bank . ticks_per_slot ( ) ,
) ;
let init_ticks = poh_recorder . tick_height ( ) ;
// Send one slot worth of ticks
for _ in 0 .. bank . ticks_per_slot ( ) {
poh_recorder . tick ( ) ;
}
// Tick should be recorded
assert_eq! (
poh_recorder . tick_height ( ) ,
init_ticks + bank . ticks_per_slot ( )
) ;
// Test that we don't reach the leader tick because of grace ticks
2019-03-18 13:24:07 -07:00
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , false ) ;
2019-03-15 13:22:16 -07:00
// reset poh now. it should discard the grace ticks wait
poh_recorder . reset (
poh_recorder . tick_height ( ) ,
bank . last_blockhash ( ) ,
1 ,
Some ( 2 ) ,
bank . ticks_per_slot ( ) ,
) ;
// without sending more ticks, we should be leader now
2019-03-18 13:24:07 -07:00
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , true ) ;
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 1 , 0 ) ;
2019-03-15 13:22:16 -07:00
// Now test that with grace ticks we can reach leader ticks
// Set the leader slot 1 slot down
poh_recorder . reset (
poh_recorder . tick_height ( ) ,
bank . last_blockhash ( ) ,
2 ,
Some ( 3 ) ,
bank . ticks_per_slot ( ) ,
) ;
// Send one slot worth of ticks
for _ in 0 .. bank . ticks_per_slot ( ) {
poh_recorder . tick ( ) ;
}
// We are not the leader yet, as expected
2019-03-18 13:24:07 -07:00
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , false ) ;
2019-03-15 13:22:16 -07:00
// Send 1 less tick than the grace ticks
for _ in 0 .. bank . ticks_per_slot ( ) / MAX_LAST_LEADER_GRACE_TICKS_FACTOR - 1 {
poh_recorder . tick ( ) ;
}
// We are still not the leader
2019-03-18 13:24:07 -07:00
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , false ) ;
2019-03-15 13:22:16 -07:00
// Send one more tick
poh_recorder . tick ( ) ;
// We should be the leader now
2019-03-18 13:24:07 -07:00
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , true ) ;
assert_eq! (
poh_recorder . reached_leader_tick ( ) . 1 ,
bank . ticks_per_slot ( ) / MAX_LAST_LEADER_GRACE_TICKS_FACTOR
) ;
// Let's test that correct grace ticks are reported
// Set the leader slot 1 slot down
poh_recorder . reset (
poh_recorder . tick_height ( ) ,
bank . last_blockhash ( ) ,
3 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
) ;
// Send remaining ticks for the slot (remember we sent extra ticks in the previous part of the test)
for _ in bank . ticks_per_slot ( ) / MAX_LAST_LEADER_GRACE_TICKS_FACTOR .. bank . ticks_per_slot ( ) {
poh_recorder . tick ( ) ;
}
// Send one extra tick before resetting (so that there's one grace tick)
poh_recorder . tick ( ) ;
// We are not the leader yet, as expected
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , false ) ;
poh_recorder . reset (
poh_recorder . tick_height ( ) ,
bank . last_blockhash ( ) ,
3 ,
Some ( 4 ) ,
bank . ticks_per_slot ( ) ,
) ;
// without sending more ticks, we should be leader now
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , true ) ;
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 1 , 1 ) ;
2019-03-19 14:49:36 -07:00
// Let's test that if a node overshoots the ticks for its target
// leader slot, reached_leader_tick() will return false
// Set the leader slot 1 slot down
poh_recorder . reset (
poh_recorder . tick_height ( ) ,
bank . last_blockhash ( ) ,
4 ,
Some ( 5 ) ,
bank . ticks_per_slot ( ) ,
) ;
// Send remaining ticks for the slot (remember we sent extra ticks in the previous part of the test)
for _ in 0 .. 4 * bank . ticks_per_slot ( ) {
poh_recorder . tick ( ) ;
}
// We are not the leader, as expected
assert_eq! ( poh_recorder . reached_leader_tick ( ) . 0 , false ) ;
2019-03-15 13:22:16 -07:00
}
2018-09-26 05:52:13 -07:00
}