1use crate::poh::Poh;
6use dlopen::symbor::{Container, SymBorApi, Symbol};
7use dlopen_derive::SymBorApi;
8use log::*;
9use rand::{thread_rng, Rng};
10use rayon::prelude::*;
11use rayon::ThreadPool;
12use serde::{Deserialize, Serialize};
13use gemachain_measure::measure::Measure;
14use gemachain_merkle_tree::MerkleTree;
15use gemachain_metrics::*;
16use gemachain_perf::cuda_runtime::PinnedVec;
17use gemachain_perf::perf_libs;
18use gemachain_perf::recycler::Recycler;
19use gemachain_rayon_threadlimit::get_thread_count;
20use gemachain_sdk::hash::Hash;
21use gemachain_sdk::timing;
22use gemachain_sdk::transaction::{Result, SanitizedTransaction, Transaction, VersionedTransaction};
23use std::cell::RefCell;
24use std::ffi::OsStr;
25use std::sync::mpsc::{Receiver, Sender};
26use std::sync::Once;
27use std::sync::{Arc, Mutex};
28use std::thread::JoinHandle;
29use std::time::Instant;
30use std::{cmp, thread};
31
32thread_local!(static PAR_THREAD_POOL: RefCell<ThreadPool> = RefCell::new(rayon::ThreadPoolBuilder::new()
33 .num_threads(get_thread_count())
34 .thread_name(|ix| format!("entry_{}", ix))
35 .build()
36 .unwrap()));
37
38pub type EntrySender = Sender<Vec<Entry>>;
39pub type EntryReceiver = Receiver<Vec<Entry>>;
40
41static mut API: Option<Container<Api>> = None;
42
43pub fn init_poh() {
44 init(OsStr::new("libpoh-simd.so"));
45}
46
47fn init(name: &OsStr) {
48 static INIT_HOOK: Once = Once::new();
49
50 info!("Loading {:?}", name);
51 unsafe {
52 INIT_HOOK.call_once(|| {
53 let path;
54 let lib_name = if let Some(perf_libs_path) = gemachain_perf::perf_libs::locate_perf_libs()
55 {
56 gemachain_perf::perf_libs::append_to_ld_library_path(
57 perf_libs_path.to_str().unwrap_or("").to_string(),
58 );
59 path = perf_libs_path.join(name);
60 path.as_os_str()
61 } else {
62 name
63 };
64
65 API = Container::load(lib_name).ok();
66 })
67 }
68}
69
70pub fn api() -> Option<&'static Container<Api<'static>>> {
71 {
72 static INIT_HOOK: Once = Once::new();
73 INIT_HOOK.call_once(|| {
74 if std::env::var("TEST_PERF_LIBS").is_ok() {
75 init_poh()
76 }
77 })
78 }
79
80 unsafe { API.as_ref() }
81}
82
83#[derive(SymBorApi)]
84pub struct Api<'a> {
85 pub poh_verify_many_simd_avx512skx:
86 Symbol<'a, unsafe extern "C" fn(hashes: *mut u8, num_hashes: *const u64)>,
87 pub poh_verify_many_simd_avx2:
88 Symbol<'a, unsafe extern "C" fn(hashes: *mut u8, num_hashes: *const u64)>,
89}
90
91#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq, Clone)]
107pub struct Entry {
108 pub num_hashes: u64,
110
111 pub hash: Hash,
113
114 pub transactions: Vec<VersionedTransaction>,
118}
119
120pub enum EntryType {
122 Transactions(Vec<SanitizedTransaction>),
123 Tick(Hash),
124}
125
126impl Entry {
127 pub fn new(prev_hash: &Hash, mut num_hashes: u64, transactions: Vec<Transaction>) -> Self {
129 if num_hashes == 0 && !transactions.is_empty() {
132 num_hashes = 1;
133 }
134
135 let transactions = transactions.into_iter().map(Into::into).collect::<Vec<_>>();
136 let hash = next_hash(prev_hash, num_hashes, &transactions);
137 Entry {
138 num_hashes,
139 hash,
140 transactions,
141 }
142 }
143
144 pub fn new_mut(
145 start_hash: &mut Hash,
146 num_hashes: &mut u64,
147 transactions: Vec<Transaction>,
148 ) -> Self {
149 let entry = Self::new(start_hash, *num_hashes, transactions);
150 *start_hash = entry.hash;
151 *num_hashes = 0;
152
153 entry
154 }
155
156 #[cfg(test)]
157 pub fn new_tick(num_hashes: u64, hash: &Hash) -> Self {
158 Entry {
159 num_hashes,
160 hash: *hash,
161 transactions: vec![],
162 }
163 }
164
165 pub fn verify(&self, start_hash: &Hash) -> bool {
168 let ref_hash = next_hash(start_hash, self.num_hashes, &self.transactions);
169 if self.hash != ref_hash {
170 warn!(
171 "next_hash is invalid expected: {:?} actual: {:?}",
172 self.hash, ref_hash
173 );
174 return false;
175 }
176 true
177 }
178
179 pub fn is_tick(&self) -> bool {
180 self.transactions.is_empty()
181 }
182}
183
184pub fn hash_transactions(transactions: &[VersionedTransaction]) -> Hash {
185 let signatures: Vec<_> = transactions
187 .iter()
188 .flat_map(|tx| tx.signatures.iter())
189 .collect();
190 let merkle_tree = MerkleTree::new(&signatures);
191 if let Some(root_hash) = merkle_tree.get_root() {
192 *root_hash
193 } else {
194 Hash::default()
195 }
196}
197
198pub fn next_hash(
203 start_hash: &Hash,
204 num_hashes: u64,
205 transactions: &[VersionedTransaction],
206) -> Hash {
207 if num_hashes == 0 && transactions.is_empty() {
208 return *start_hash;
209 }
210
211 let mut poh = Poh::new(*start_hash, None);
212 poh.hash(num_hashes.saturating_sub(1));
213 if transactions.is_empty() {
214 poh.tick().unwrap().hash
215 } else {
216 poh.record(hash_transactions(transactions)).unwrap().hash
217 }
218}
219
220enum VerifyAction {
222 Mixin(Hash),
224 Tick,
226 None,
228}
229
230pub struct GpuVerificationData {
231 thread_h: Option<JoinHandle<u64>>,
232 hashes: Option<Arc<Mutex<PinnedVec<Hash>>>>,
233 verifications: Option<Vec<(VerifyAction, Hash)>>,
234}
235
236pub enum DeviceVerificationData {
237 Cpu(),
238 Gpu(GpuVerificationData),
239}
240
241pub struct EntryVerificationState {
242 verification_status: EntryVerificationStatus,
243 poh_duration_us: u64,
244 device_verification_data: DeviceVerificationData,
245}
246
247#[derive(Default, Clone)]
248pub struct VerifyRecyclers {
249 hash_recycler: Recycler<PinnedVec<Hash>>,
250 tick_count_recycler: Recycler<PinnedVec<u64>>,
251}
252
253#[derive(PartialEq, Clone, Copy, Debug)]
254pub enum EntryVerificationStatus {
255 Failure,
256 Success,
257 Pending,
258}
259
260impl EntryVerificationState {
261 pub fn status(&self) -> EntryVerificationStatus {
262 self.verification_status
263 }
264
265 pub fn poh_duration_us(&self) -> u64 {
266 self.poh_duration_us
267 }
268
269 pub fn finish_verify(&mut self) -> bool {
270 match &mut self.device_verification_data {
271 DeviceVerificationData::Gpu(verification_state) => {
272 let gpu_time_us = verification_state.thread_h.take().unwrap().join().unwrap();
273
274 let mut verify_check_time = Measure::start("verify_check");
275 let hashes = verification_state.hashes.take().unwrap();
276 let hashes = Arc::try_unwrap(hashes)
277 .expect("unwrap Arc")
278 .into_inner()
279 .expect("into_inner");
280 let res = PAR_THREAD_POOL.with(|thread_pool| {
281 thread_pool.borrow().install(|| {
282 hashes
283 .into_par_iter()
284 .cloned()
285 .zip(verification_state.verifications.take().unwrap())
286 .all(|(hash, (action, expected))| {
287 let actual = match action {
288 VerifyAction::Mixin(mixin) => {
289 Poh::new(hash, None).record(mixin).unwrap().hash
290 }
291 VerifyAction::Tick => Poh::new(hash, None).tick().unwrap().hash,
292 VerifyAction::None => hash,
293 };
294 actual == expected
295 })
296 })
297 });
298
299 verify_check_time.stop();
300 self.poh_duration_us += gpu_time_us + verify_check_time.as_us();
301
302 self.verification_status = if res {
303 EntryVerificationStatus::Success
304 } else {
305 EntryVerificationStatus::Failure
306 };
307 res
308 }
309 DeviceVerificationData::Cpu() => {
310 self.verification_status == EntryVerificationStatus::Success
311 }
312 }
313 }
314}
315
316pub fn verify_transactions(
317 entries: Vec<Entry>,
318 verify: Arc<dyn Fn(VersionedTransaction) -> Result<SanitizedTransaction> + Send + Sync>,
319) -> Result<Vec<EntryType>> {
320 PAR_THREAD_POOL.with(|thread_pool| {
321 thread_pool.borrow().install(|| {
322 entries
323 .into_par_iter()
324 .map(|entry| {
325 if entry.transactions.is_empty() {
326 Ok(EntryType::Tick(entry.hash))
327 } else {
328 Ok(EntryType::Transactions(
329 entry
330 .transactions
331 .into_par_iter()
332 .map(verify.as_ref())
333 .collect::<Result<Vec<_>>>()?,
334 ))
335 }
336 })
337 .collect()
338 })
339 })
340}
341
342fn compare_hashes(computed_hash: Hash, ref_entry: &Entry) -> bool {
343 let actual = if !ref_entry.transactions.is_empty() {
344 let tx_hash = hash_transactions(&ref_entry.transactions);
345 let mut poh = Poh::new(computed_hash, None);
346 poh.record(tx_hash).unwrap().hash
347 } else if ref_entry.num_hashes > 0 {
348 let mut poh = Poh::new(computed_hash, None);
349 poh.tick().unwrap().hash
350 } else {
351 computed_hash
352 };
353 actual == ref_entry.hash
354}
355
356pub trait EntrySlice {
358 fn verify_cpu(&self, start_hash: &Hash) -> EntryVerificationState;
360 fn verify_cpu_generic(&self, start_hash: &Hash) -> EntryVerificationState;
361 fn verify_cpu_x86_simd(&self, start_hash: &Hash, simd_len: usize) -> EntryVerificationState;
362 fn start_verify(&self, start_hash: &Hash, recyclers: VerifyRecyclers)
363 -> EntryVerificationState;
364 fn verify(&self, start_hash: &Hash) -> bool;
365 fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool;
369 fn tick_count(&self) -> u64;
371}
372
373impl EntrySlice for [Entry] {
374 fn verify(&self, start_hash: &Hash) -> bool {
375 self.start_verify(start_hash, VerifyRecyclers::default())
376 .finish_verify()
377 }
378
379 fn verify_cpu_generic(&self, start_hash: &Hash) -> EntryVerificationState {
380 let now = Instant::now();
381 let genesis = [Entry {
382 num_hashes: 0,
383 hash: *start_hash,
384 transactions: vec![],
385 }];
386 let entry_pairs = genesis.par_iter().chain(self).zip(self);
387 let res = PAR_THREAD_POOL.with(|thread_pool| {
388 thread_pool.borrow().install(|| {
389 entry_pairs.all(|(x0, x1)| {
390 let r = x1.verify(&x0.hash);
391 if !r {
392 warn!(
393 "entry invalid!: x0: {:?}, x1: {:?} num txs: {}",
394 x0.hash,
395 x1.hash,
396 x1.transactions.len()
397 );
398 }
399 r
400 })
401 })
402 });
403
404 let poh_duration_us = timing::duration_as_us(&now.elapsed());
405 EntryVerificationState {
406 verification_status: if res {
407 EntryVerificationStatus::Success
408 } else {
409 EntryVerificationStatus::Failure
410 },
411 poh_duration_us,
412 device_verification_data: DeviceVerificationData::Cpu(),
413 }
414 }
415
416 fn verify_cpu_x86_simd(&self, start_hash: &Hash, simd_len: usize) -> EntryVerificationState {
417 use gemachain_sdk::hash::HASH_BYTES;
418 let now = Instant::now();
419 let genesis = [Entry {
420 num_hashes: 0,
421 hash: *start_hash,
422 transactions: vec![],
423 }];
424
425 let aligned_len = ((self.len() + simd_len - 1) / simd_len) * simd_len;
426 let mut hashes_bytes = vec![0u8; HASH_BYTES * aligned_len];
427 genesis
428 .iter()
429 .chain(self)
430 .enumerate()
431 .for_each(|(i, entry)| {
432 if i < self.len() {
433 let start = i * HASH_BYTES;
434 let end = start + HASH_BYTES;
435 hashes_bytes[start..end].copy_from_slice(&entry.hash.to_bytes());
436 }
437 });
438 let mut hashes_chunked: Vec<_> = hashes_bytes.chunks_mut(simd_len * HASH_BYTES).collect();
439
440 let mut num_hashes: Vec<u64> = self
441 .iter()
442 .map(|entry| entry.num_hashes.saturating_sub(1))
443 .collect();
444 num_hashes.resize(aligned_len, 0);
445 let num_hashes: Vec<_> = num_hashes.chunks(simd_len).collect();
446
447 let res = PAR_THREAD_POOL.with(|thread_pool| {
448 thread_pool.borrow().install(|| {
449 hashes_chunked
450 .par_iter_mut()
451 .zip(num_hashes)
452 .enumerate()
453 .all(|(i, (chunk, num_hashes))| {
454 match simd_len {
455 8 => unsafe {
456 (api().unwrap().poh_verify_many_simd_avx2)(
457 chunk.as_mut_ptr(),
458 num_hashes.as_ptr(),
459 );
460 },
461 16 => unsafe {
462 (api().unwrap().poh_verify_many_simd_avx512skx)(
463 chunk.as_mut_ptr(),
464 num_hashes.as_ptr(),
465 );
466 },
467 _ => {
468 panic!("unsupported simd len: {}", simd_len);
469 }
470 }
471 let entry_start = i * simd_len;
472 let entry_end = std::cmp::min(entry_start + simd_len, self.len());
475 self[entry_start..entry_end]
476 .iter()
477 .enumerate()
478 .all(|(j, ref_entry)| {
479 let start = j * HASH_BYTES;
480 let end = start + HASH_BYTES;
481 let hash = Hash::new(&chunk[start..end]);
482 compare_hashes(hash, ref_entry)
483 })
484 })
485 })
486 });
487 let poh_duration_us = timing::duration_as_us(&now.elapsed());
488 EntryVerificationState {
489 verification_status: if res {
490 EntryVerificationStatus::Success
491 } else {
492 EntryVerificationStatus::Failure
493 },
494 poh_duration_us,
495 device_verification_data: DeviceVerificationData::Cpu(),
496 }
497 }
498
499 fn verify_cpu(&self, start_hash: &Hash) -> EntryVerificationState {
500 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
501 let (has_avx2, has_avx512) = (
502 is_x86_feature_detected!("avx2"),
503 is_x86_feature_detected!("avx512f"),
504 );
505 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
506 let (has_avx2, has_avx512) = (false, false);
507
508 if api().is_some() {
509 if has_avx512 && self.len() >= 128 {
510 self.verify_cpu_x86_simd(start_hash, 16)
511 } else if has_avx2 && self.len() >= 48 {
512 self.verify_cpu_x86_simd(start_hash, 8)
513 } else {
514 self.verify_cpu_generic(start_hash)
515 }
516 } else {
517 self.verify_cpu_generic(start_hash)
518 }
519 }
520
521 fn start_verify(
522 &self,
523 start_hash: &Hash,
524 recyclers: VerifyRecyclers,
525 ) -> EntryVerificationState {
526 let start = Instant::now();
527 let api = perf_libs::api();
528 if api.is_none() {
529 return self.verify_cpu(start_hash);
530 }
531 let api = api.unwrap();
532 inc_new_counter_info!("entry_verify-num_entries", self.len() as usize);
533
534 let genesis = [Entry {
535 num_hashes: 0,
536 hash: *start_hash,
537 transactions: vec![],
538 }];
539
540 let hashes: Vec<Hash> = genesis
541 .iter()
542 .chain(self)
543 .map(|entry| entry.hash)
544 .take(self.len())
545 .collect();
546
547 let mut hashes_pinned = recyclers.hash_recycler.allocate("poh_verify_hash");
548 hashes_pinned.set_pinnable();
549 hashes_pinned.resize(hashes.len(), Hash::default());
550 hashes_pinned.copy_from_slice(&hashes);
551
552 let mut num_hashes_vec = recyclers
553 .tick_count_recycler
554 .allocate("poh_verify_num_hashes");
555 num_hashes_vec.reserve_and_pin(cmp::max(1, self.len()));
556 for entry in self {
557 num_hashes_vec.push(entry.num_hashes.saturating_sub(1));
558 }
559
560 let length = self.len();
561 let hashes = Arc::new(Mutex::new(hashes_pinned));
562 let hashes_clone = hashes.clone();
563
564 let gpu_verify_thread = thread::spawn(move || {
565 let mut hashes = hashes_clone.lock().unwrap();
566 let gpu_wait = Instant::now();
567 let res;
568 unsafe {
569 res = (api.poh_verify_many)(
570 hashes.as_mut_ptr() as *mut u8,
571 num_hashes_vec.as_ptr(),
572 length,
573 1,
574 );
575 }
576 if res != 0 {
577 panic!("GPU PoH verify many failed");
578 }
579 inc_new_counter_info!(
580 "entry_verify-gpu_thread",
581 timing::duration_as_us(&gpu_wait.elapsed()) as usize
582 );
583 timing::duration_as_us(&gpu_wait.elapsed())
584 });
585
586 let verifications = PAR_THREAD_POOL.with(|thread_pool| {
587 thread_pool.borrow().install(|| {
588 self.into_par_iter()
589 .map(|entry| {
590 let answer = entry.hash;
591 let action = if entry.transactions.is_empty() {
592 if entry.num_hashes == 0 {
593 VerifyAction::None
594 } else {
595 VerifyAction::Tick
596 }
597 } else {
598 VerifyAction::Mixin(hash_transactions(&entry.transactions))
599 };
600 (action, answer)
601 })
602 .collect()
603 })
604 });
605
606 let device_verification_data = DeviceVerificationData::Gpu(GpuVerificationData {
607 thread_h: Some(gpu_verify_thread),
608 verifications: Some(verifications),
609 hashes: Some(hashes),
610 });
611 EntryVerificationState {
612 verification_status: EntryVerificationStatus::Pending,
613 poh_duration_us: timing::duration_as_us(&start.elapsed()),
614 device_verification_data,
615 }
616 }
617
618 fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool {
619 if hashes_per_tick == 0 {
621 return true;
622 }
623
624 for entry in self {
625 *tick_hash_count = tick_hash_count.saturating_add(entry.num_hashes);
626 if entry.is_tick() {
627 if *tick_hash_count != hashes_per_tick {
628 warn!(
629 "invalid tick hash count!: entry: {:#?}, tick_hash_count: {}, hashes_per_tick: {}",
630 entry,
631 tick_hash_count,
632 hashes_per_tick
633 );
634 return false;
635 }
636 *tick_hash_count = 0;
637 }
638 }
639 *tick_hash_count < hashes_per_tick
640 }
641
642 fn tick_count(&self) -> u64 {
643 self.iter().filter(|e| e.is_tick()).count() as u64
644 }
645}
646
647pub fn next_entry_mut(start: &mut Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry {
648 let entry = Entry::new(start, num_hashes, transactions);
649 *start = entry.hash;
650 entry
651}
652
653#[allow(clippy::same_item_push)]
654pub fn create_ticks(num_ticks: u64, hashes_per_tick: u64, mut hash: Hash) -> Vec<Entry> {
655 let mut ticks = Vec::with_capacity(num_ticks as usize);
656 for _ in 0..num_ticks {
657 let new_tick = next_entry_mut(&mut hash, hashes_per_tick, vec![]);
658 ticks.push(new_tick);
659 }
660
661 ticks
662}
663
664#[allow(clippy::same_item_push)]
665pub fn create_random_ticks(num_ticks: u64, max_hashes_per_tick: u64, mut hash: Hash) -> Vec<Entry> {
666 let mut ticks = Vec::with_capacity(num_ticks as usize);
667 for _ in 0..num_ticks {
668 let hashes_per_tick = thread_rng().gen_range(1, max_hashes_per_tick);
669 let new_tick = next_entry_mut(&mut hash, hashes_per_tick, vec![]);
670 ticks.push(new_tick);
671 }
672
673 ticks
674}
675
676pub fn next_entry(prev_hash: &Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry {
678 assert!(num_hashes > 0 || transactions.is_empty());
679 let transactions = transactions.into_iter().map(Into::into).collect::<Vec<_>>();
680 Entry {
681 num_hashes,
682 hash: next_hash(prev_hash, num_hashes, &transactions),
683 transactions,
684 }
685}
686
687#[cfg(test)]
688mod tests {
689 use super::*;
690 use gemachain_sdk::{
691 hash::{hash, Hash},
692 pubkey::Pubkey,
693 signature::{Keypair, Signer},
694 system_transaction,
695 };
696
697 #[test]
698 fn test_entry_verify() {
699 let zero = Hash::default();
700 let one = hash(zero.as_ref());
701 assert!(Entry::new_tick(0, &zero).verify(&zero)); assert!(!Entry::new_tick(0, &zero).verify(&one)); assert!(next_entry(&zero, 1, vec![]).verify(&zero)); assert!(!next_entry(&zero, 1, vec![]).verify(&one)); }
706
707 #[test]
708 fn test_transaction_reorder_attack() {
709 let zero = Hash::default();
710
711 let keypair = Keypair::new();
713 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
714 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
715 let mut e0 = Entry::new(&zero, 0, vec![tx0.clone(), tx1.clone()]);
716 assert!(e0.verify(&zero));
717
718 e0.transactions[0] = tx1.into(); e0.transactions[1] = tx0.into();
721 assert!(!e0.verify(&zero));
722 }
723
724 #[test]
725 fn test_transaction_signing() {
726 use gemachain_sdk::signature::Signature;
727 let zero = Hash::default();
728
729 let keypair = Keypair::new();
730 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
731 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
732
733 let mut e0 = vec![Entry::new(&zero, 0, vec![tx0, tx1])];
735 assert!(e0.verify(&zero));
736
737 let orig_sig = e0[0].transactions[0].signatures[0];
739 e0[0].transactions[0].signatures[0] = Signature::default();
740 assert!(!e0.verify(&zero));
741
742 e0[0].transactions[0].signatures[0] = orig_sig;
744 assert!(e0.verify(&zero));
745
746 let len = e0[0].transactions[0].signatures.len();
748 e0[0].transactions[0]
749 .signatures
750 .resize(len - 1, Signature::default());
751 assert!(!e0.verify(&zero));
752
753 let e0 = vec![Entry::new(&zero, 0, vec![])];
755 assert!(e0.verify(&zero));
756 }
757
758 #[test]
759 fn test_next_entry() {
760 let zero = Hash::default();
761 let tick = next_entry(&zero, 1, vec![]);
762 assert_eq!(tick.num_hashes, 1);
763 assert_ne!(tick.hash, zero);
764
765 let tick = next_entry(&zero, 0, vec![]);
766 assert_eq!(tick.num_hashes, 0);
767 assert_eq!(tick.hash, zero);
768
769 let keypair = Keypair::new();
770 let tx0 = system_transaction::transfer(&keypair, &Pubkey::new_unique(), 42, zero);
771 let entry0 = next_entry(&zero, 1, vec![tx0.clone()]);
772 assert_eq!(entry0.num_hashes, 1);
773 assert_eq!(entry0.hash, next_hash(&zero, 1, &[tx0.into()]));
774 }
775
776 #[test]
777 #[should_panic]
778 fn test_next_entry_panic() {
779 let zero = Hash::default();
780 let keypair = Keypair::new();
781 let tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
782 next_entry(&zero, 0, vec![tx]);
783 }
784
785 #[test]
786 fn test_verify_slice1() {
787 gemachain_logger::setup();
788 let zero = Hash::default();
789 let one = hash(zero.as_ref());
790 assert!(vec![][..].verify(&zero)); assert!(vec![Entry::new_tick(0, &zero)][..].verify(&zero)); assert!(!vec![Entry::new_tick(0, &zero)][..].verify(&one)); assert!(vec![next_entry(&zero, 0, vec![]); 2][..].verify(&zero)); let mut bad_ticks = vec![next_entry(&zero, 0, vec![]); 2];
796 bad_ticks[1].hash = one;
797 assert!(!bad_ticks.verify(&zero)); }
799
800 #[test]
801 fn test_verify_slice_with_hashes1() {
802 gemachain_logger::setup();
803 let zero = Hash::default();
804 let one = hash(zero.as_ref());
805 let two = hash(one.as_ref());
806 assert!(vec![][..].verify(&one)); assert!(vec![Entry::new_tick(1, &two)][..].verify(&one)); assert!(!vec![Entry::new_tick(1, &two)][..].verify(&two)); let mut ticks = vec![next_entry(&one, 1, vec![])];
811 ticks.push(next_entry(&ticks.last().unwrap().hash, 1, vec![]));
812 assert!(ticks.verify(&one)); let mut bad_ticks = vec![next_entry(&one, 1, vec![])];
815 bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![]));
816 bad_ticks[1].hash = one;
817 assert!(!bad_ticks.verify(&one)); }
819
820 #[test]
821 fn test_verify_slice_with_hashes_and_transactions() {
822 gemachain_logger::setup();
823 let zero = Hash::default();
824 let one = hash(zero.as_ref());
825 let two = hash(one.as_ref());
826 let alice_keypair = Keypair::new();
827 let bob_keypair = Keypair::new();
828 let tx0 = system_transaction::transfer(&alice_keypair, &bob_keypair.pubkey(), 1, one);
829 let tx1 = system_transaction::transfer(&bob_keypair, &alice_keypair.pubkey(), 1, one);
830 assert!(vec![][..].verify(&one)); assert!(vec![next_entry(&one, 1, vec![tx0.clone()])][..].verify(&one)); assert!(!vec![next_entry(&one, 1, vec![tx0.clone()])][..].verify(&two)); let mut ticks = vec![next_entry(&one, 1, vec![tx0.clone()])];
835 ticks.push(next_entry(
836 &ticks.last().unwrap().hash,
837 1,
838 vec![tx1.clone()],
839 ));
840 assert!(ticks.verify(&one)); let mut bad_ticks = vec![next_entry(&one, 1, vec![tx0])];
843 bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![tx1]));
844 bad_ticks[1].hash = one;
845 assert!(!bad_ticks.verify(&one)); }
847
848 #[test]
849 fn test_verify_tick_hash_count() {
850 let hashes_per_tick = 10;
851 let tx = VersionedTransaction::default();
852
853 let no_hash_tx_entry = Entry {
854 transactions: vec![tx.clone()],
855 ..Entry::default()
856 };
857 let single_hash_tx_entry = Entry {
858 transactions: vec![tx.clone()],
859 num_hashes: 1,
860 ..Entry::default()
861 };
862 let partial_tx_entry = Entry {
863 num_hashes: hashes_per_tick - 1,
864 transactions: vec![tx.clone()],
865 ..Entry::default()
866 };
867 let full_tx_entry = Entry {
868 num_hashes: hashes_per_tick,
869 transactions: vec![tx.clone()],
870 ..Entry::default()
871 };
872 let max_hash_tx_entry = Entry {
873 transactions: vec![tx],
874 num_hashes: u64::MAX,
875 ..Entry::default()
876 };
877
878 let no_hash_tick_entry = Entry::new_tick(0, &Hash::default());
879 let single_hash_tick_entry = Entry::new_tick(1, &Hash::default());
880 let partial_tick_entry = Entry::new_tick(hashes_per_tick - 1, &Hash::default());
881 let full_tick_entry = Entry::new_tick(hashes_per_tick, &Hash::default());
882 let max_hash_tick_entry = Entry::new_tick(u64::MAX, &Hash::default());
883
884 let mut tick_hash_count = 0;
886 let mut entries = vec![];
887 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
888 assert_eq!(tick_hash_count, 0);
889
890 tick_hash_count = hashes_per_tick;
892 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
893 assert_eq!(tick_hash_count, hashes_per_tick);
894 tick_hash_count = 0;
895
896 entries = vec![max_hash_tx_entry.clone()];
898 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, 0));
899 assert_eq!(tick_hash_count, 0);
900
901 entries = vec![partial_tick_entry.clone()];
903 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
904 assert_eq!(tick_hash_count, hashes_per_tick - 1);
905 tick_hash_count = 0;
906
907 entries = vec![no_hash_tx_entry, full_tick_entry.clone()];
909 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
910 assert_eq!(tick_hash_count, 0);
911
912 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick - 1));
914 assert_eq!(tick_hash_count, hashes_per_tick);
915 tick_hash_count = 0;
916
917 entries = vec![partial_tx_entry];
919 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
920 assert_eq!(tick_hash_count, hashes_per_tick - 1);
921 tick_hash_count = 0;
922
923 entries = vec![full_tx_entry.clone(), no_hash_tick_entry];
925 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
926 assert_eq!(tick_hash_count, 0);
927
928 entries = vec![full_tx_entry.clone(), single_hash_tick_entry.clone()];
930 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
931 assert_eq!(tick_hash_count, hashes_per_tick + 1);
932 tick_hash_count = 0;
933
934 entries = vec![full_tx_entry];
936 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
937 assert_eq!(tick_hash_count, hashes_per_tick);
938 tick_hash_count = 0;
939
940 entries = vec![single_hash_tx_entry.clone(), partial_tick_entry];
942 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
943 assert_eq!(tick_hash_count, 0);
944
945 let tx_entries: Vec<Entry> = (0..hashes_per_tick - 1)
947 .map(|_| single_hash_tx_entry.clone())
948 .collect();
949 entries = [tx_entries, vec![single_hash_tick_entry]].concat();
950 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
951 assert_eq!(tick_hash_count, 0);
952
953 entries = vec![full_tick_entry.clone(), max_hash_tick_entry];
955 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
956 assert_eq!(tick_hash_count, u64::MAX);
957 tick_hash_count = 0;
958
959 entries = vec![max_hash_tx_entry, full_tick_entry];
961 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
962 assert_eq!(tick_hash_count, u64::MAX);
963 }
964
965 #[test]
966 fn test_poh_verify_fuzz() {
967 gemachain_logger::setup();
968 for _ in 0..100 {
969 let mut time = Measure::start("ticks");
970 let num_ticks = thread_rng().gen_range(1, 100);
971 info!("create {} ticks:", num_ticks);
972 let mut entries = create_random_ticks(num_ticks, 100, Hash::default());
973 time.stop();
974
975 let mut modified = false;
976 if thread_rng().gen_ratio(1, 2) {
977 modified = true;
978 let modify_idx = thread_rng().gen_range(0, num_ticks) as usize;
979 entries[modify_idx].hash = hash(&[1, 2, 3]);
980 }
981
982 info!("done.. {}", time);
983 let mut time = Measure::start("poh");
984 let res = entries.verify(&Hash::default());
985 assert_eq!(res, !modified);
986 time.stop();
987 info!("{} {}", time, res);
988 }
989 }
990}