| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -18,12 +18,11 @@ use solana_sdk::hash::Hash;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				use solana_sdk::timing;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				use solana_sdk::transaction::Transaction;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				use std::cell::RefCell;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				use std::cmp;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				use std::sync::mpsc::{Receiver, Sender};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				use std::sync::{Arc, Mutex};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				use std::thread;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				use std::thread::JoinHandle;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				use std::time::Instant;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				use std::{cmp, thread};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				thread_local!(static PAR_THREAD_POOL: RefCell<ThreadPool> = RefCell::new(rayon::ThreadPoolBuilder::new()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    .num_threads(get_thread_count())
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -151,10 +150,10 @@ pub fn next_hash(start_hash: &Hash, num_hashes: u64, transactions: &[Transaction
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				pub struct EntryVerifyState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				pub struct VerificationData {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    thread_h: Option<JoinHandle<u64>>,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    verification_status: EntryVerificationStatus,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    hashes: Option<Arc<Mutex<PinnedVec<Hash>>>>,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    verified: bool,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    tx_hashes: Vec<Option<Hash>>,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    start_time_ms: u64,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -165,13 +164,33 @@ pub struct VerifyRecyclers {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    tick_count_recycler: Recycler<PinnedVec<u64>>,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				impl EntryVerifyState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#[derive(PartialEq, Clone, Copy, Debug)]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				pub enum EntryVerificationStatus {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Failure,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Success,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Pending,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				pub enum EntryVerificationState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    CPU(VerificationData),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    GPU(VerificationData),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				impl EntryVerificationState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    pub fn status(&self) -> EntryVerificationStatus {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        match self {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            EntryVerificationState::CPU(state) => state.verification_status,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            EntryVerificationState::GPU(state) => state.verification_status,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    pub fn finish_verify(&mut self, entries: &[Entry]) -> bool {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if self.hashes.is_some() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            let gpu_time_ms = self.thread_h.take().unwrap().join().unwrap();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        match self {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            EntryVerificationState::GPU(verification_state) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                let gpu_time_ms = verification_state.thread_h.take().unwrap().join().unwrap();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                let mut verify_check_time = Measure::start("verify_check");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            let hashes = self.hashes.take().expect("hashes.as_ref");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                let hashes = verification_state.hashes.take().expect("hashes.as_ref");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                let hashes = Arc::try_unwrap(hashes)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    .expect("unwrap Arc")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    .into_inner()
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -180,7 +199,7 @@ impl EntryVerifyState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    thread_pool.borrow().install(|| {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        hashes
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                            .into_par_iter()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        .zip(&self.tx_hashes)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                            .zip(&verification_state.tx_hashes)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                            .zip(entries)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                            .all(|((hash, tx_hash), answer)| {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                                if answer.num_hashes == 0 {
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -200,11 +219,20 @@ impl EntryVerifyState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                verify_check_time.stop();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                inc_new_counter_warn!(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    "entry_verify-duration",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                (gpu_time_ms + verify_check_time.as_ms() + self.start_time_ms) as usize
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    (gpu_time_ms + verify_check_time.as_ms() + verification_state.start_time_ms)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        as usize
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            res
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                verification_state.verification_status = if res {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    EntryVerificationStatus::Success
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            self.verified
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    EntryVerificationStatus::Failure
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                res
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            EntryVerificationState::CPU(verification_state) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                verification_state.verification_status == EntryVerificationStatus::Success
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -212,8 +240,9 @@ impl EntryVerifyState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// an EntrySlice is a slice of Entries
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				pub trait EntrySlice {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /// Verifies the hashes and counts of a slice of transactions are all consistent.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    fn verify_cpu(&self, start_hash: &Hash) -> EntryVerifyState;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    fn start_verify(&self, start_hash: &Hash, recyclers: VerifyRecyclers) -> EntryVerifyState;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    fn verify_cpu(&self, start_hash: &Hash) -> EntryVerificationState;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    fn start_verify(&self, start_hash: &Hash, recyclers: VerifyRecyclers)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        -> EntryVerificationState;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    fn verify(&self, start_hash: &Hash) -> bool;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /// Checks that each entry tick has the correct number of hashes. Entry slices do not
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /// necessarily end in a tick, so `tick_hash_count` is used to carry over the hash count
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -228,7 +257,7 @@ impl EntrySlice for [Entry] {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        self.start_verify(start_hash, VerifyRecyclers::default())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            .finish_verify(self)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    fn verify_cpu(&self, start_hash: &Hash) -> EntryVerifyState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    fn verify_cpu(&self, start_hash: &Hash) -> EntryVerificationState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let now = Instant::now();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let genesis = [Entry {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            num_hashes: 0,
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -256,16 +285,24 @@ impl EntrySlice for [Entry] {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            "entry_verify-duration",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            timing::duration_as_ms(&now.elapsed()) as usize
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        EntryVerifyState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        EntryVerificationState::CPU(VerificationData {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            thread_h: None,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            verified: res,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            verification_status: if res {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                EntryVerificationStatus::Success
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                EntryVerificationStatus::Failure
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            hashes: None,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            tx_hashes: vec![],
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            start_time_ms: 0,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        })
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    fn start_verify(&self, start_hash: &Hash, recyclers: VerifyRecyclers) -> EntryVerifyState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    fn start_verify(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        &self,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        start_hash: &Hash,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        recyclers: VerifyRecyclers,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ) -> EntryVerificationState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let api = perf_libs::api();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if api.is_none() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return self.verify_cpu(start_hash);
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -341,13 +378,13 @@ impl EntrySlice for [Entry] {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            })
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        EntryVerifyState {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        EntryVerificationState::GPU(VerificationData {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            thread_h: Some(gpu_verify_thread),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            verified: false,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            verification_status: EntryVerificationStatus::Pending,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            tx_hashes,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            start_time_ms: timing::duration_as_ms(&start.elapsed()),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            hashes: Some(hashes),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        })
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool {
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -515,14 +552,17 @@ mod tests {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        solana_logger::setup();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let zero = Hash::default();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let one = hash(&zero.as_ref());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(vec![][..].verify(&zero)); // base case
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(vec![Entry::new_tick(0, &zero)][..].verify(&zero)); // singleton case 1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(!vec![Entry::new_tick(0, &zero)][..].verify(&one)); // singleton case 2, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(vec![next_entry(&zero, 0, vec![]); 2][..].verify(&zero)); // inductive step
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(vec![][..].verify(&zero), true); // base case
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(vec![Entry::new_tick(0, &zero)][..].verify(&zero), true); // singleton case 1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(vec![Entry::new_tick(0, &zero)][..].verify(&one), false); // singleton case 2, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            vec![next_entry(&zero, 0, vec![]); 2][..].verify(&zero),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            true
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ); // inductive step
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let mut bad_ticks = vec![next_entry(&zero, 0, vec![]); 2];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        bad_ticks[1].hash = one;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(!bad_ticks.verify(&zero)); // inductive step, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(bad_ticks.verify(&zero), false); // inductive step, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    #[test]
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -531,18 +571,18 @@ mod tests {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let zero = Hash::default();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let one = hash(&zero.as_ref());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let two = hash(&one.as_ref());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(vec![][..].verify(&one)); // base case
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(vec![Entry::new_tick(1, &two)][..].verify(&one)); // singleton case 1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(!vec![Entry::new_tick(1, &two)][..].verify(&two)); // singleton case 2, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(vec![][..].verify(&one), true); // base case
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(vec![Entry::new_tick(1, &two)][..].verify(&one), true); // singleton case 1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(vec![Entry::new_tick(1, &two)][..].verify(&two), false); // singleton case 2, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let mut ticks = vec![next_entry(&one, 1, vec![])];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ticks.push(next_entry(&ticks.last().unwrap().hash, 1, vec![]));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(ticks.verify(&one)); // inductive step
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(ticks.verify(&one), true); // inductive step
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let mut bad_ticks = vec![next_entry(&one, 1, vec![])];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![]));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        bad_ticks[1].hash = one;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(!bad_ticks.verify(&one)); // inductive step, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(bad_ticks.verify(&one), false); // inductive step, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    #[test]
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -554,9 +594,15 @@ mod tests {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let alice_pubkey = Keypair::default();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let tx0 = create_sample_payment(&alice_pubkey, one);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let tx1 = create_sample_timestamp(&alice_pubkey, one);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(vec![][..].verify(&one)); // base case
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(vec![next_entry(&one, 1, vec![tx0.clone()])][..].verify(&one)); // singleton case 1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(!vec![next_entry(&one, 1, vec![tx0.clone()])][..].verify(&two)); // singleton case 2, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(vec![][..].verify(&one), true); // base case
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            vec![next_entry(&one, 1, vec![tx0.clone()])][..].verify(&one),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            true
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ); // singleton case 1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            vec![next_entry(&one, 1, vec![tx0.clone()])][..].verify(&two),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ); // singleton case 2, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let mut ticks = vec![next_entry(&one, 1, vec![tx0.clone()])];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ticks.push(next_entry(
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -564,12 +610,12 @@ mod tests {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            1,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            vec![tx1.clone()],
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(ticks.verify(&one)); // inductive step
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(ticks.verify(&one), true); // inductive step
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let mut bad_ticks = vec![next_entry(&one, 1, vec![tx0])];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![tx1]));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        bad_ticks[1].hash = one;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert!(!bad_ticks.verify(&one)); // inductive step, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        assert_eq!(bad_ticks.verify(&one), false); // inductive step, bad
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    #[test]
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				 
 |