Add GPU based PoH verification (#4524)
* Add GPU poh verify * Switch to single PoH verify function * Add EntrySlice verify tests with hashes and txs * Add poh-verify benchmarks
This commit is contained in:
		
							
								
								
									
										48
									
								
								core/benches/poh_verify.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								core/benches/poh_verify.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					#![feature(test)]
 | 
				
			||||||
 | 
					extern crate test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use solana::entry::EntrySlice;
 | 
				
			||||||
 | 
					use solana::entry::{next_entry_mut, Entry};
 | 
				
			||||||
 | 
					use solana_sdk::hash::{hash, Hash};
 | 
				
			||||||
 | 
					use solana_sdk::signature::{Keypair, KeypairUtil};
 | 
				
			||||||
 | 
					use solana_sdk::system_transaction;
 | 
				
			||||||
 | 
					use test::Bencher;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const NUM_HASHES: u64 = 400;
 | 
				
			||||||
 | 
					const NUM_ENTRIES: usize = 800;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[bench]
 | 
				
			||||||
 | 
					fn bench_poh_verify_ticks(bencher: &mut Bencher) {
 | 
				
			||||||
 | 
					    let zero = Hash::default();
 | 
				
			||||||
 | 
					    let mut cur_hash = hash(&zero.as_ref());
 | 
				
			||||||
 | 
					    let start = *&cur_hash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut ticks: Vec<Entry> = Vec::with_capacity(NUM_ENTRIES);
 | 
				
			||||||
 | 
					    for _ in 0..NUM_ENTRIES {
 | 
				
			||||||
 | 
					        ticks.push(next_entry_mut(&mut cur_hash, NUM_HASHES, vec![]));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bencher.iter(|| {
 | 
				
			||||||
 | 
					        ticks.verify(&start);
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[bench]
 | 
				
			||||||
 | 
					fn bench_poh_verify_transaction_entries(bencher: &mut Bencher) {
 | 
				
			||||||
 | 
					    let zero = Hash::default();
 | 
				
			||||||
 | 
					    let mut cur_hash = hash(&zero.as_ref());
 | 
				
			||||||
 | 
					    let start = *&cur_hash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let keypair1 = Keypair::new();
 | 
				
			||||||
 | 
					    let pubkey1 = keypair1.pubkey();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut ticks: Vec<Entry> = Vec::with_capacity(NUM_ENTRIES);
 | 
				
			||||||
 | 
					    for _ in 0..NUM_ENTRIES {
 | 
				
			||||||
 | 
					        let tx = system_transaction::create_user_account(&keypair1, &pubkey1, 42, cur_hash);
 | 
				
			||||||
 | 
					        ticks.push(next_entry_mut(&mut cur_hash, NUM_HASHES, vec![tx]));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bencher.iter(|| {
 | 
				
			||||||
 | 
					        ticks.verify(&start);
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -16,6 +16,13 @@ use std::borrow::Borrow;
 | 
				
			|||||||
use std::sync::mpsc::{Receiver, Sender};
 | 
					use std::sync::mpsc::{Receiver, Sender};
 | 
				
			||||||
use std::sync::{Arc, RwLock};
 | 
					use std::sync::{Arc, RwLock};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "cuda")]
 | 
				
			||||||
 | 
					use crate::sigverify::poh_verify_many;
 | 
				
			||||||
 | 
					#[cfg(feature = "cuda")]
 | 
				
			||||||
 | 
					use std::sync::Mutex;
 | 
				
			||||||
 | 
					#[cfg(feature = "cuda")]
 | 
				
			||||||
 | 
					use std::thread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub type EntrySender = Sender<Vec<Entry>>;
 | 
					pub type EntrySender = Sender<Vec<Entry>>;
 | 
				
			||||||
pub type EntryReceiver = Receiver<Vec<Entry>>;
 | 
					pub type EntryReceiver = Receiver<Vec<Entry>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -212,6 +219,7 @@ pub trait EntrySlice {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl EntrySlice for [Entry] {
 | 
					impl EntrySlice for [Entry] {
 | 
				
			||||||
 | 
					    #[cfg(not(feature = "cuda"))]
 | 
				
			||||||
    fn verify(&self, start_hash: &Hash) -> bool {
 | 
					    fn verify(&self, start_hash: &Hash) -> bool {
 | 
				
			||||||
        let genesis = [Entry {
 | 
					        let genesis = [Entry {
 | 
				
			||||||
            num_hashes: 0,
 | 
					            num_hashes: 0,
 | 
				
			||||||
@@ -233,6 +241,78 @@ impl EntrySlice for [Entry] {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[cfg(feature = "cuda")]
 | 
				
			||||||
 | 
					    fn verify(&self, start_hash: &Hash) -> bool {
 | 
				
			||||||
 | 
					        let genesis = [Entry {
 | 
				
			||||||
 | 
					            num_hashes: 0,
 | 
				
			||||||
 | 
					            hash: *start_hash,
 | 
				
			||||||
 | 
					            transactions: vec![],
 | 
				
			||||||
 | 
					        }];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let hashes: Vec<Hash> = genesis
 | 
				
			||||||
 | 
					            .par_iter()
 | 
				
			||||||
 | 
					            .chain(self)
 | 
				
			||||||
 | 
					            .map(|entry| entry.hash)
 | 
				
			||||||
 | 
					            .take(self.len())
 | 
				
			||||||
 | 
					            .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let num_hashes_vec: Vec<u64> = self
 | 
				
			||||||
 | 
					            .into_iter()
 | 
				
			||||||
 | 
					            .map(|entry| entry.num_hashes.saturating_sub(1))
 | 
				
			||||||
 | 
					            .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let length = self.len();
 | 
				
			||||||
 | 
					        let hashes = Arc::new(Mutex::new(hashes));
 | 
				
			||||||
 | 
					        let hashes_clone = hashes.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let gpu_verify_thread = thread::spawn(move || {
 | 
				
			||||||
 | 
					            let mut hashes = hashes_clone.lock().unwrap();
 | 
				
			||||||
 | 
					            let res;
 | 
				
			||||||
 | 
					            unsafe {
 | 
				
			||||||
 | 
					                res = poh_verify_many(
 | 
				
			||||||
 | 
					                    hashes.as_mut_ptr() as *mut u8,
 | 
				
			||||||
 | 
					                    num_hashes_vec.as_ptr(),
 | 
				
			||||||
 | 
					                    length,
 | 
				
			||||||
 | 
					                    1,
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if res != 0 {
 | 
				
			||||||
 | 
					                panic!("GPU PoH verify many failed");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx_hashes: Vec<Option<Hash>> = self
 | 
				
			||||||
 | 
					            .into_par_iter()
 | 
				
			||||||
 | 
					            .map(|entry| {
 | 
				
			||||||
 | 
					                if entry.transactions.is_empty() {
 | 
				
			||||||
 | 
					                    None
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Some(hash_transactions(&entry.transactions))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gpu_verify_thread.join().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let hashes = Arc::try_unwrap(hashes).unwrap().into_inner().unwrap();
 | 
				
			||||||
 | 
					        hashes
 | 
				
			||||||
 | 
					            .into_par_iter()
 | 
				
			||||||
 | 
					            .zip(tx_hashes)
 | 
				
			||||||
 | 
					            .zip(self)
 | 
				
			||||||
 | 
					            .all(|((hash, tx_hash), answer)| {
 | 
				
			||||||
 | 
					                if answer.num_hashes == 0 {
 | 
				
			||||||
 | 
					                    hash == answer.hash
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    let mut poh = Poh::new(hash, None);
 | 
				
			||||||
 | 
					                    if let Some(mixin) = tx_hash {
 | 
				
			||||||
 | 
					                        poh.record(mixin).unwrap().hash == answer.hash
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        poh.tick().unwrap().hash == answer.hash
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn to_blobs(&self) -> Vec<Blob> {
 | 
					    fn to_blobs(&self) -> Vec<Blob> {
 | 
				
			||||||
        split_serializable_chunks(
 | 
					        split_serializable_chunks(
 | 
				
			||||||
            &self,
 | 
					            &self,
 | 
				
			||||||
@@ -556,6 +636,53 @@ mod tests {
 | 
				
			|||||||
        assert!(!bad_ticks.verify(&zero)); // inductive step, bad
 | 
					        assert!(!bad_ticks.verify(&zero)); // inductive step, bad
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_verify_slice_with_hashes() {
 | 
				
			||||||
 | 
					        solana_logger::setup();
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_verify_slice_with_hashes_and_transactions() {
 | 
				
			||||||
 | 
					        solana_logger::setup();
 | 
				
			||||||
 | 
					        let zero = Hash::default();
 | 
				
			||||||
 | 
					        let one = hash(&zero.as_ref());
 | 
				
			||||||
 | 
					        let two = hash(&one.as_ref());
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut ticks = vec![next_entry(&one, 1, vec![tx0.clone()])];
 | 
				
			||||||
 | 
					        ticks.push(next_entry(
 | 
				
			||||||
 | 
					            &ticks.last().unwrap().hash,
 | 
				
			||||||
 | 
					            1,
 | 
				
			||||||
 | 
					            vec![tx1.clone()],
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					        assert!(ticks.verify(&one)); // 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
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn blob_sized_entries(num_entries: usize) -> Vec<Entry> {
 | 
					    fn blob_sized_entries(num_entries: usize) -> Vec<Entry> {
 | 
				
			||||||
        // rough guess
 | 
					        // rough guess
 | 
				
			||||||
        let mut magic_len = BLOB_DATA_SIZE
 | 
					        let mut magic_len = BLOB_DATA_SIZE
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,9 @@ use solana_sdk::signature::Signature;
 | 
				
			|||||||
use solana_sdk::transaction::Transaction;
 | 
					use solana_sdk::transaction::Transaction;
 | 
				
			||||||
use std::mem::size_of;
 | 
					use std::mem::size_of;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "cuda")]
 | 
				
			||||||
 | 
					use std::os::raw::c_int;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const NUM_THREADS: u32 = 10;
 | 
					pub const NUM_THREADS: u32 = 10;
 | 
				
			||||||
use std::cell::RefCell;
 | 
					use std::cell::RefCell;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -68,6 +71,13 @@ extern "C" {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    pub fn chacha_init_sha_state(sha_state: *mut u8, num_keys: u32);
 | 
					    pub fn chacha_init_sha_state(sha_state: *mut u8, num_keys: u32);
 | 
				
			||||||
    pub fn chacha_end_sha_state(sha_state_in: *const u8, out: *mut u8, num_keys: u32);
 | 
					    pub fn chacha_end_sha_state(sha_state_in: *const u8, out: *mut u8, num_keys: u32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn poh_verify_many(
 | 
				
			||||||
 | 
					        hashes: *mut u8,
 | 
				
			||||||
 | 
					        num_hashes_arr: *const u64,
 | 
				
			||||||
 | 
					        num_elems: usize,
 | 
				
			||||||
 | 
					        use_non_default_stream: u8,
 | 
				
			||||||
 | 
					    ) -> c_int;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(not(feature = "cuda"))]
 | 
					#[cfg(not(feature = "cuda"))]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ if [[ ! -d target/perf-libs ]]; then
 | 
				
			|||||||
  (
 | 
					  (
 | 
				
			||||||
    set -x
 | 
					    set -x
 | 
				
			||||||
    cd target/perf-libs
 | 
					    cd target/perf-libs
 | 
				
			||||||
    curl https://solana-perf.s3.amazonaws.com/v0.12.1/x86_64-unknown-linux-gnu/solana-perf.tgz | tar zxvf -
 | 
					    curl https://solana-perf.s3.amazonaws.com/v0.13.2/x86_64-unknown-linux-gnu/solana-perf.tgz | tar zxvf -
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,15 @@
 | 
				
			|||||||
//! The `hash` module provides functions for creating SHA-256 hashes.
 | 
					//! The `hash` module provides functions for creating SHA-256 hashes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use bs58;
 | 
					use bs58;
 | 
				
			||||||
use generic_array::typenum::U32;
 | 
					 | 
				
			||||||
use generic_array::GenericArray;
 | 
					 | 
				
			||||||
use sha2::{Digest, Sha256};
 | 
					use sha2::{Digest, Sha256};
 | 
				
			||||||
 | 
					use std::convert::TryFrom;
 | 
				
			||||||
use std::fmt;
 | 
					use std::fmt;
 | 
				
			||||||
use std::mem;
 | 
					use std::mem;
 | 
				
			||||||
use std::str::FromStr;
 | 
					use std::str::FromStr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
 | 
					#[derive(Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
 | 
				
			||||||
pub struct Hash(GenericArray<u8, U32>);
 | 
					#[repr(transparent)]
 | 
				
			||||||
 | 
					pub struct Hash([u8; 32]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Default)]
 | 
					#[derive(Clone, Default)]
 | 
				
			||||||
pub struct Hasher {
 | 
					pub struct Hasher {
 | 
				
			||||||
@@ -28,9 +28,7 @@ impl Hasher {
 | 
				
			|||||||
    pub fn result(self) -> Hash {
 | 
					    pub fn result(self) -> Hash {
 | 
				
			||||||
        // At the time of this writing, the sha2 library is stuck on an old version
 | 
					        // At the time of this writing, the sha2 library is stuck on an old version
 | 
				
			||||||
        // of generic_array (0.9.0). Decouple ourselves with a clone to our version.
 | 
					        // of generic_array (0.9.0). Decouple ourselves with a clone to our version.
 | 
				
			||||||
        Hash(GenericArray::clone_from_slice(
 | 
					        Hash(<[u8; 32]>::try_from(self.hasher.result().as_slice()).unwrap())
 | 
				
			||||||
            self.hasher.result().as_slice(),
 | 
					 | 
				
			||||||
        ))
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,7 +73,7 @@ impl FromStr for Hash {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl Hash {
 | 
					impl Hash {
 | 
				
			||||||
    pub fn new(hash_slice: &[u8]) -> Self {
 | 
					    pub fn new(hash_slice: &[u8]) -> Self {
 | 
				
			||||||
        Hash(GenericArray::clone_from_slice(&hash_slice))
 | 
					        Hash(<[u8; 32]>::try_from(hash_slice).unwrap())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user