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
//!
2018-12-07 19:16:27 -08:00
use crate ::bank ::Bank ;
use crate ::entry ::Entry ;
use crate ::poh ::Poh ;
use crate ::result ::{ Error , Result } ;
2018-11-16 08:04:46 -08:00
use solana_sdk ::hash ::Hash ;
2018-11-29 16:18:47 -08:00
use solana_sdk ::transaction ::Transaction ;
2018-09-26 05:52:13 -07:00
use std ::sync ::mpsc ::Sender ;
use std ::sync ::{ Arc , Mutex } ;
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 ,
}
2018-09-26 05:52:13 -07:00
#[ derive(Clone) ]
pub struct PohRecorder {
poh : Arc < Mutex < Poh > > ,
bank : Arc < Bank > ,
sender : Sender < Vec < Entry > > ,
2018-11-08 12:55:23 -08:00
max_tick_height : Option < u64 > ,
2018-09-26 05:52:13 -07:00
}
impl PohRecorder {
2018-10-18 22:57:48 -07:00
pub fn hash ( & self ) -> Result < ( ) > {
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
let mut poh = self . poh . lock ( ) . unwrap ( ) ;
2018-12-12 18:52:11 -08:00
self . check_tick_height ( & poh ) ? ;
poh . hash ( ) ;
Ok ( ( ) )
2018-09-26 05:52:13 -07:00
}
2018-10-25 14:56:21 -07:00
pub fn tick ( & mut self ) -> Result < ( ) > {
2018-10-18 22:57:48 -07:00
// Register and send the entry out while holding the lock if the max PoH height
// hasn't been reached.
2018-09-26 05:52:13 -07:00
// This guarantees PoH order and Entry production and banks LastId queue is the same
let mut poh = self . poh . lock ( ) . unwrap ( ) ;
2018-12-12 18:52:11 -08:00
self . check_tick_height ( & poh ) ? ;
self . register_and_send_tick ( & mut * poh )
2018-09-26 05:52:13 -07:00
}
pub fn record ( & self , mixin : Hash , txs : Vec < Transaction > ) -> Result < ( ) > {
// Register and send the entry out while holding the lock.
// This guarantees PoH order and Entry production and banks LastId queue is the same.
let mut poh = self . poh . lock ( ) . unwrap ( ) ;
2018-12-12 18:52:11 -08:00
self . check_tick_height ( & poh ) ? ;
self . record_and_send_txs ( & mut * poh , mixin , txs )
2018-10-18 22:57:48 -07:00
}
2018-10-25 14:56:21 -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 (
bank : Arc < Bank > ,
sender : Sender < Vec < Entry > > ,
last_entry_id : Hash ,
max_tick_height : Option < u64 > ,
) -> Self {
2018-11-05 09:47:41 -08:00
let poh = Arc ::new ( Mutex ::new ( Poh ::new ( last_entry_id , bank . tick_height ( ) ) ) ) ;
2018-10-25 14:56:21 -07:00
PohRecorder {
poh ,
bank ,
sender ,
max_tick_height ,
}
}
2018-12-12 18:52:11 -08:00
fn check_tick_height ( & self , poh : & Poh ) -> Result < ( ) > {
match self . max_tick_height {
Some ( max_tick_height ) if poh . tick_height > = max_tick_height = > {
Err ( Error ::PohRecorderError ( PohRecorderError ::MaxHeightReached ) )
}
_ = > Ok ( ( ) ) ,
2018-10-18 22:57:48 -07:00
}
}
fn record_and_send_txs ( & self , poh : & mut Poh , mixin : Hash , txs : Vec < Transaction > ) -> Result < ( ) > {
2018-11-12 17:03:23 -08:00
let entry = 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 " ) ;
2018-09-26 05:52:13 -07:00
let entry = Entry {
2018-12-10 20:03:04 -08:00
tick_height : entry . tick_height ,
2018-11-12 17:03:23 -08:00
num_hashes : entry . num_hashes ,
id : entry . id ,
2018-09-26 05:52:13 -07:00
transactions : txs ,
} ;
self . sender . send ( vec! [ entry ] ) ? ;
Ok ( ( ) )
}
2018-10-18 22:57:48 -07:00
fn register_and_send_tick ( & self , poh : & mut Poh ) -> Result < ( ) > {
2018-11-12 17:03:23 -08:00
let tick = poh . tick ( ) ;
let tick = Entry {
2018-12-10 20:03:04 -08:00
tick_height : tick . tick_height ,
2018-11-12 17:03:23 -08:00
num_hashes : tick . num_hashes ,
id : tick . id ,
transactions : vec ! [ ] ,
} ;
self . bank . register_tick ( & tick . id ) ;
self . sender . send ( vec! [ tick ] ) ? ;
2018-10-18 22:57:48 -07:00
Ok ( ( ) )
}
2018-09-26 05:52:13 -07:00
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2019-01-24 12:04:04 -08:00
use crate ::genesis_block ::GenesisBlock ;
2018-12-07 19:16:27 -08:00
use crate ::test_tx ::test_tx ;
2018-11-16 08:04:46 -08:00
use solana_sdk ::hash ::hash ;
2018-09-26 05:52:13 -07:00
use std ::sync ::mpsc ::channel ;
use std ::sync ::Arc ;
#[ test ]
2019-01-31 13:53:08 -08:00
fn test_poh_recorder ( ) {
2019-01-24 12:04:04 -08:00
let ( genesis_block , _mint_keypair ) = GenesisBlock ::new ( 1 ) ;
let bank = Arc ::new ( Bank ::new ( & genesis_block ) ) ;
2018-11-12 17:03:23 -08:00
let prev_id = bank . last_id ( ) ;
2018-09-26 05:52:13 -07:00
let ( entry_sender , entry_receiver ) = channel ( ) ;
2019-01-31 13:53:08 -08:00
let mut poh_recorder = PohRecorder ::new ( bank , entry_sender , prev_id , Some ( 2 ) ) ;
2018-09-26 05:52:13 -07:00
//send some data
let h1 = hash ( b " hello world! " ) ;
2018-10-10 17:23:06 -07:00
let tx = test_tx ( ) ;
2019-01-28 14:52:35 -08:00
poh_recorder . record ( h1 , vec! [ tx . clone ( ) ] ) . unwrap ( ) ;
2018-12-10 20:03:04 -08:00
//get some events
let e = entry_receiver . recv ( ) . unwrap ( ) ;
2019-01-31 13:53:08 -08:00
assert_eq! ( e [ 0 ] . tick_height , 0 ) ; // super weird case, but ok!
2018-12-10 20:03:04 -08:00
2019-01-28 14:52:35 -08:00
poh_recorder . tick ( ) . unwrap ( ) ;
2018-12-10 20:03:04 -08:00
let e = entry_receiver . recv ( ) . unwrap ( ) ;
2019-01-31 13:53:08 -08:00
assert_eq! ( e [ 0 ] . tick_height , 1 ) ;
2018-09-26 05:52:13 -07:00
2019-01-28 14:52:35 -08:00
poh_recorder . tick ( ) . unwrap ( ) ;
2018-12-10 20:03:04 -08:00
let e = entry_receiver . recv ( ) . unwrap ( ) ;
2019-01-31 13:53:08 -08:00
assert_eq! ( e [ 0 ] . tick_height , 2 ) ;
2018-09-26 05:52:13 -07:00
2018-12-12 18:52:11 -08:00
// max tick height reached
assert! ( poh_recorder . tick ( ) . is_err ( ) ) ;
assert! ( poh_recorder . record ( h1 , vec! [ tx ] ) . is_err ( ) ) ;
2018-09-26 05:52:13 -07:00
//make sure it handles channel close correctly
drop ( entry_receiver ) ;
assert! ( poh_recorder . tick ( ) . is_err ( ) ) ;
}
}