1use {
6 crate::poh::Poh,
7 crossbeam_channel::{Receiver, Sender},
8 log::*,
9 rayon::{ThreadPool, prelude::*},
10 smallvec::SmallVec,
11 solana_address::Address,
12 solana_cost_model::shred_limit::DEFAULT_MAX_DATA_SHREDS_PER_SLOT,
13 solana_hash::Hash,
14 solana_merkle_tree::MerkleTree,
15 solana_runtime_transaction::transaction_with_meta::TransactionWithMeta,
16 solana_signature::Signature,
17 solana_transaction::{Transaction, versioned::VersionedTransaction},
18 solana_transaction_error::{TransactionError, TransactionResult as Result},
19 std::{iter::repeat_with, time::Instant},
20 wincode::{SchemaRead, SchemaWrite, containers::Vec as WincodeVec, len::BincodeLen},
21};
22
23pub type EntrySender = Sender<Vec<Entry>>;
24pub type EntryReceiver = Receiver<Vec<Entry>>;
25
26pub const MAX_DATA_SHREDS_SIZE: usize =
27 DEFAULT_MAX_DATA_SHREDS_PER_SLOT as usize * solana_packet::PACKET_DATA_SIZE;
28pub type MaxDataShredsLen = BincodeLen<MAX_DATA_SHREDS_SIZE>;
29
30#[derive(Debug, Default, PartialEq, Eq, Clone, SchemaWrite, SchemaRead)]
45pub struct Entry {
46 pub num_hashes: u64,
48
49 pub hash: Hash,
51
52 #[wincode(with = "WincodeVec<VersionedTransaction, MaxDataShredsLen>")]
56 pub transactions: Vec<VersionedTransaction>,
57}
58
59#[derive(Clone, Debug, Default, PartialEq, Eq)]
61pub struct EntryVerificationData {
62 pub num_hashes: u64,
63 pub hash: Hash,
64 pub num_transactions: usize,
65 pub signatures: Vec<Signature>,
66}
67
68impl From<&Entry> for EntryVerificationData {
69 fn from(entry: &Entry) -> Self {
70 Self {
71 num_hashes: entry.num_hashes,
72 hash: entry.hash,
73 num_transactions: entry.transactions.len(),
74 signatures: entry
75 .transactions
76 .iter()
77 .flat_map(|tx| tx.signatures.iter().copied())
78 .collect(),
79 }
80 }
81}
82
83impl EntryVerificationData {
84 pub fn verify(&self, start_hash: &Hash) -> bool {
85 let ref_hash = next_hash_with_signatures(
86 start_hash,
87 self.num_hashes,
88 self.num_transactions,
89 &self.signatures,
90 );
91 if self.hash != ref_hash {
92 warn!(
93 "next_hash is invalid expected: {:?} actual: {:?}",
94 self.hash, ref_hash
95 );
96 return false;
97 }
98 true
99 }
100}
101
102pub fn entries_to_verification_data(entries: &[Entry]) -> Vec<EntryVerificationData> {
103 entries.iter().map(Into::into).collect()
104}
105
106pub struct EntrySummary {
107 pub num_hashes: u64,
108 pub hash: Hash,
109 pub num_transactions: u64,
110}
111
112impl From<&Entry> for EntrySummary {
113 fn from(entry: &Entry) -> Self {
114 Self {
115 num_hashes: entry.num_hashes,
116 hash: entry.hash,
117 num_transactions: entry.transactions.len() as u64,
118 }
119 }
120}
121
122pub enum EntryType<Tx: TransactionWithMeta> {
124 Transactions(Vec<Tx>),
125 Tick(Hash),
126}
127
128#[derive(Clone, Debug, PartialEq, Eq)]
129struct TxVerificationData {
130 is_simple_vote: bool,
131 signatures: SmallVec<[Signature; 2]>,
132 signer_pubkeys: SmallVec<[Address; 2]>,
133 message_hash: Hash,
134 serialized_message: Vec<u8>,
135}
136
137#[inline]
139pub fn batch_verify<'a, I>(items: I) -> bool
140where
141 I: IntoParallelIterator<Item = (&'a Signature, &'a Address, &'a [u8])>,
142{
143 items
144 .into_par_iter()
145 .all(|(signature, pubkey, message)| signature.verify(pubkey.as_ref(), message))
146}
147
148pub struct UnverifiedSignatures {
149 signatures: Vec<TxVerificationData>,
150}
151
152impl UnverifiedSignatures {
153 fn with_capacity(capacity: usize) -> Self {
154 Self {
155 signatures: Vec::with_capacity(capacity),
156 }
157 }
158
159 pub fn verify(&self) -> Result<()> {
160 let verification_items = self.signatures.par_iter().flat_map_iter(|tx| {
161 let message = tx.serialized_message.as_slice();
162 let len = tx.signatures.len();
163
164 (0..len).map(move |i| (&tx.signatures[i], &tx.signer_pubkeys[i], message))
165 });
166
167 if batch_verify(verification_items) {
168 Ok(())
169 } else {
170 Err(TransactionError::SignatureFailure)
171 }
172 }
173
174 #[cfg(feature = "dev-context-only-utils")]
175 pub fn verify_single_loop_for_benches(&self) -> Result<()> {
177 self.signatures.par_iter().try_for_each(|tx_signatures| {
178 if tx_signatures
179 .signatures
180 .iter()
181 .zip(tx_signatures.signer_pubkeys.iter())
182 .all(|(signature, pubkey)| {
183 signature.verify(pubkey.as_ref(), &tx_signatures.serialized_message)
184 })
185 {
186 Ok(())
187 } else {
188 Err(TransactionError::SignatureFailure)
189 }
190 })
191 }
192
193 pub fn vote_transaction_message_hashes(&self) -> Vec<Hash> {
194 self.signatures
195 .iter()
196 .filter(|tx_signatures| tx_signatures.is_simple_vote)
197 .filter_map(|tx_signatures| {
198 tx_signatures
199 .signatures
200 .first()
201 .map(|_| tx_signatures.message_hash)
202 })
203 .collect()
204 }
205}
206
207pub struct ValidatedHashedTransactions<Tx: TransactionWithMeta> {
208 pub entries: Vec<EntryType<Tx>>,
209 pub unverified_signatures: UnverifiedSignatures,
210}
211
212impl Entry {
213 pub fn new(prev_hash: &Hash, mut num_hashes: u64, transactions: Vec<Transaction>) -> Self {
215 if num_hashes == 0 && !transactions.is_empty() {
218 num_hashes = 1;
219 }
220
221 let transactions = transactions.into_iter().map(Into::into).collect::<Vec<_>>();
222 let hash = next_hash(prev_hash, num_hashes, &transactions);
223 Entry {
224 num_hashes,
225 hash,
226 transactions,
227 }
228 }
229
230 pub fn new_mut(
231 start_hash: &mut Hash,
232 num_hashes: &mut u64,
233 transactions: Vec<Transaction>,
234 ) -> Self {
235 let entry = Self::new(start_hash, *num_hashes, transactions);
236 *start_hash = entry.hash;
237 *num_hashes = 0;
238
239 entry
240 }
241
242 #[cfg(test)]
243 pub fn new_tick(num_hashes: u64, hash: &Hash) -> Self {
244 Entry {
245 num_hashes,
246 hash: *hash,
247 transactions: vec![],
248 }
249 }
250
251 pub fn verify(&self, start_hash: &Hash) -> bool {
254 EntryVerificationData::from(self).verify(start_hash)
255 }
256
257 pub fn is_tick(&self) -> bool {
258 self.transactions.is_empty()
259 }
260}
261
262pub fn hash_signatures(signatures: &[impl AsRef<[u8]>]) -> Hash {
263 let merkle_tree = MerkleTree::new(signatures);
264 if let Some(root_hash) = merkle_tree.get_root() {
265 *root_hash
266 } else {
267 Hash::default()
268 }
269}
270
271pub fn hash_transactions(transactions: &[VersionedTransaction]) -> Hash {
272 let signatures: Vec<_> = transactions
274 .iter()
275 .flat_map(|tx| tx.signatures.iter())
276 .collect();
277 hash_signatures(&signatures)
278}
279
280fn next_hash_with_signatures(
281 start_hash: &Hash,
282 num_hashes: u64,
283 num_transactions: usize,
284 signatures: &[Signature],
285) -> Hash {
286 if num_hashes == 0 && num_transactions == 0 {
287 return *start_hash;
288 }
289
290 let mut poh = Poh::new(*start_hash, None);
291 poh.hash(num_hashes.saturating_sub(1));
292 if num_transactions == 0 {
293 poh.tick().unwrap().hash
294 } else {
295 poh.record(hash_signatures(signatures)).unwrap().hash
296 }
297}
298
299pub fn next_hash(
303 start_hash: &Hash,
304 num_hashes: u64,
305 transactions: &[VersionedTransaction],
306) -> Hash {
307 let signatures: Vec<_> = transactions
308 .iter()
309 .flat_map(|tx| tx.signatures.iter().copied())
310 .collect();
311 next_hash_with_signatures(start_hash, num_hashes, transactions.len(), &signatures)
312}
313
314pub struct EntryVerificationState {
315 verification_status: bool,
316 poh_duration_us: u64,
317}
318
319impl EntryVerificationState {
320 pub fn status(&self) -> bool {
321 self.verification_status
322 }
323
324 pub fn poh_duration_us(&self) -> u64 {
325 self.poh_duration_us
326 }
327}
328
329fn validate_and_hash_entry_transactions<Tx: TransactionWithMeta, F>(
330 entry: Entry,
331 verify: &F,
332 unverified_signatures: &mut UnverifiedSignatures,
333) -> Result<EntryType<Tx>>
334where
335 F: Fn(VersionedTransaction, &[u8]) -> Result<Tx>,
336{
337 if entry.transactions.is_empty() {
338 return Ok(EntryType::Tick(entry.hash));
339 }
340
341 let verified_transactions = entry
342 .transactions
343 .into_iter()
344 .map(|versioned_tx| {
345 let num_signers = usize::from(versioned_tx.message.header().num_required_signatures);
346 let static_account_keys = versioned_tx.message.static_account_keys();
347 if static_account_keys.len() < num_signers {
348 return Err(TransactionError::SanitizeFailure);
349 }
350 let signatures = versioned_tx.signatures.iter().copied().collect();
351 let signer_pubkeys = static_account_keys[..num_signers].iter().copied().collect();
352 let serialized_message = versioned_tx.message.serialize();
353 let verified_transaction = verify(versioned_tx, &serialized_message)?;
354 let message_hash = *verified_transaction.message_hash();
355 unverified_signatures.signatures.push(TxVerificationData {
356 is_simple_vote: verified_transaction.is_simple_vote_transaction(),
357 signatures,
358 message_hash,
359 serialized_message,
360 signer_pubkeys,
361 });
362
363 Ok(verified_transaction)
364 })
365 .collect::<Result<Vec<_>>>()?;
366 Ok(EntryType::Transactions(verified_transactions))
367}
368
369pub fn validate_and_hash_transactions<Tx: TransactionWithMeta + Send + Sync, F>(
374 entries: Vec<Entry>,
375 num_txs: usize,
376 thread_pool: &ThreadPool,
377 verify: F,
378) -> Result<ValidatedHashedTransactions<Tx>>
379where
380 F: Fn(VersionedTransaction, &[u8]) -> Result<Tx> + Send + Sync,
381{
382 const PARALLEL_VERIFY_THRESHOLD: usize = 200;
383 if num_txs < PARALLEL_VERIFY_THRESHOLD {
384 let mut unverified_signatures = UnverifiedSignatures::with_capacity(num_txs);
385 let entries = entries
386 .into_iter()
387 .map(|entry| {
388 validate_and_hash_entry_transactions(entry, &verify, &mut unverified_signatures)
389 })
390 .collect::<Result<_>>()?;
391 return Ok(ValidatedHashedTransactions {
392 entries,
393 unverified_signatures,
394 });
395 }
396
397 let verified = thread_pool.install(|| {
398 entries
399 .into_par_iter()
400 .map(|entry| {
401 let mut unverified_signatures =
402 UnverifiedSignatures::with_capacity(entry.transactions.len());
403 let verified_entry = validate_and_hash_entry_transactions(
404 entry,
405 &verify,
406 &mut unverified_signatures,
407 )?;
408 Ok((verified_entry, unverified_signatures))
409 })
410 .collect::<Result<Vec<_>>>()
411 })?;
412
413 let mut entries = Vec::with_capacity(verified.len());
414 let mut unverified_signatures = UnverifiedSignatures::with_capacity(num_txs);
415 for (entry, mut tx_unverified_signatures) in verified {
416 entries.push(entry);
417 unverified_signatures
418 .signatures
419 .append(&mut tx_unverified_signatures.signatures);
420 }
421 Ok(ValidatedHashedTransactions {
422 entries,
423 unverified_signatures,
424 })
425}
426
427pub fn verify_entries_cpu_in_pool(
428 entries: &[EntryVerificationData],
429 start_hash: &Hash,
430 thread_pool: &ThreadPool,
431) -> EntryVerificationState {
432 thread_pool.install(|| verify_entries_cpu(entries, start_hash))
433}
434
435fn verify_entries_cpu_generic(
436 entries: &[EntryVerificationData],
437 start_hash: &Hash,
438) -> EntryVerificationState {
439 let now = Instant::now();
440 let genesis = [EntryVerificationData {
441 num_hashes: 0,
442 hash: *start_hash,
443 num_transactions: 0,
444 signatures: Vec::new(),
445 }];
446 let entry_pairs = genesis.par_iter().chain(entries).zip(entries);
447 let res = entry_pairs.all(|(x0, x1)| {
448 let r = x1.verify(&x0.hash);
449 if !r {
450 warn!(
451 "entry invalid!: x0: {:?}, x1: {:?} num txs: {}",
452 x0.hash, x1.hash, x1.num_transactions
453 );
454 }
455 r
456 });
457 let poh_duration_us = now.elapsed().as_micros() as u64;
458 EntryVerificationState {
459 verification_status: res,
460 poh_duration_us,
461 }
462}
463
464pub fn verify_entries_cpu(
465 entries: &[EntryVerificationData],
466 start_hash: &Hash,
467) -> EntryVerificationState {
468 verify_entries_cpu_generic(entries, start_hash)
469}
470
471pub trait EntrySlice {
473 fn verify_cpu(&self, start_hash: &Hash) -> EntryVerificationState;
475 fn verify_cpu_generic(&self, start_hash: &Hash) -> EntryVerificationState;
476 fn verify(&self, start_hash: &Hash, thread_pool: &ThreadPool) -> EntryVerificationState;
477 fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool;
481 fn tick_count(&self) -> u64;
483}
484
485impl EntrySlice for [Entry] {
486 fn verify(&self, start_hash: &Hash, thread_pool: &ThreadPool) -> EntryVerificationState {
487 let verification_entries = entries_to_verification_data(self);
488 verify_entries_cpu_in_pool(&verification_entries, start_hash, thread_pool)
489 }
490
491 fn verify_cpu_generic(&self, start_hash: &Hash) -> EntryVerificationState {
492 let verification_entries = entries_to_verification_data(self);
493 verify_entries_cpu_generic(&verification_entries, start_hash)
494 }
495
496 fn verify_cpu(&self, start_hash: &Hash) -> EntryVerificationState {
497 let verification_entries = entries_to_verification_data(self);
498 verify_entries_cpu(&verification_entries, start_hash)
499 }
500
501 fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool {
502 if hashes_per_tick == 0 {
504 return true;
505 }
506
507 for entry in self {
508 *tick_hash_count = tick_hash_count.saturating_add(entry.num_hashes);
509 if entry.is_tick() {
510 if entry.num_hashes == 0 {
511 return false;
512 }
513 if *tick_hash_count != hashes_per_tick {
514 warn!(
515 "invalid tick hash count!: entry: {entry:#?}, tick_hash_count: \
516 {tick_hash_count}, hashes_per_tick: {hashes_per_tick}"
517 );
518 return false;
519 }
520 *tick_hash_count = 0;
521 }
522 }
523 *tick_hash_count < hashes_per_tick
524 }
525
526 fn tick_count(&self) -> u64 {
527 self.iter().filter(|e| e.is_tick()).count() as u64
528 }
529}
530
531pub fn next_entry_mut(start: &mut Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry {
532 let entry = Entry::new(start, num_hashes, transactions);
533 *start = entry.hash;
534 entry
535}
536
537pub fn create_ticks(num_ticks: u64, hashes_per_tick: u64, mut hash: Hash) -> Vec<Entry> {
538 repeat_with(|| next_entry_mut(&mut hash, hashes_per_tick, vec![]))
539 .take(num_ticks as usize)
540 .collect()
541}
542
543pub fn next_entry(prev_hash: &Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry {
545 let transactions = transactions.into_iter().map(Into::into).collect::<Vec<_>>();
546 next_versioned_entry(prev_hash, num_hashes, transactions)
547}
548
549pub fn next_versioned_entry(
551 prev_hash: &Hash,
552 num_hashes: u64,
553 transactions: Vec<VersionedTransaction>,
554) -> Entry {
555 assert!(num_hashes > 0 || transactions.is_empty());
556 Entry {
557 num_hashes,
558 hash: next_hash(prev_hash, num_hashes, &transactions),
559 transactions,
560 }
561}
562
563pub fn thread_pool_for_tests() -> ThreadPool {
564 rayon::ThreadPoolBuilder::new()
569 .num_threads(4)
570 .thread_name(|i| format!("solEntryTest{i:02}"))
571 .build()
572 .expect("new rayon threadpool")
573}
574
575#[cfg(feature = "dev-context-only-utils")]
576pub fn thread_pool_for_benches() -> ThreadPool {
577 rayon::ThreadPoolBuilder::new()
578 .num_threads(num_cpus::get())
579 .thread_name(|i| format!("solEntryBnch{i:02}"))
580 .build()
581 .expect("new rayon threadpool")
582}
583
584#[cfg(test)]
585mod tests {
586 use {
587 super::*,
588 agave_reserved_account_keys::ReservedAccountKeys,
589 rand::{Rng, rng},
590 rayon::ThreadPoolBuilder,
591 solana_hash::Hash,
592 solana_keypair::Keypair,
593 solana_measure::measure::Measure,
594 solana_message::{
595 MessageHeader, SimpleAddressLoader, VersionedMessage,
596 compiled_instruction::CompiledInstruction, v1,
597 },
598 solana_perf::test_tx::test_tx,
599 solana_pubkey::Pubkey,
600 solana_runtime_transaction::runtime_transaction::RuntimeTransaction,
601 solana_sha256_hasher::hash,
602 solana_signature::Signature,
603 solana_signer::Signer,
604 solana_system_transaction as system_transaction,
605 solana_transaction::{
606 sanitized::{MessageHash, SanitizedTransaction},
607 versioned::VersionedTransaction,
608 },
609 solana_transaction_error::TransactionResult as Result,
610 };
611
612 fn simple_v1_transaction_for_deserialization_tests() -> VersionedTransaction {
613 VersionedTransaction {
614 signatures: vec![Signature::default()],
615 message: VersionedMessage::V1(v1::Message {
616 header: MessageHeader {
617 num_required_signatures: 1,
618 num_readonly_signed_accounts: 0,
619 num_readonly_unsigned_accounts: 1,
620 },
621 config: v1::TransactionConfig::empty(),
622 lifetime_specifier: Hash::new_unique(),
623 account_keys: vec![Pubkey::new_unique(), Pubkey::new_unique()],
624 instructions: vec![CompiledInstruction {
625 program_id_index: 1,
626 accounts: vec![0],
627 data: vec![],
628 }],
629 }),
630 }
631 }
632
633 fn create_random_ticks(num_ticks: u64, max_hashes_per_tick: u64, mut hash: Hash) -> Vec<Entry> {
634 repeat_with(|| {
635 let hashes_per_tick = rng().random_range(1..max_hashes_per_tick);
636 next_entry_mut(&mut hash, hashes_per_tick, vec![])
637 })
638 .take(num_ticks as usize)
639 .collect()
640 }
641
642 #[test]
643 fn test_entry_verify() {
644 let zero = Hash::default();
645 let one = hash(zero.as_ref());
646 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)); }
651
652 fn test_verify_transactions<Tx: TransactionWithMeta + Send + Sync + 'static>(
653 entries: Vec<Entry>,
654 skip_verification: bool,
655 thread_pool: &ThreadPool,
656 verify: impl Fn(VersionedTransaction, &[u8]) -> Result<Tx> + Send + Sync,
657 ) -> bool {
658 let num_txs = entries.iter().map(|entry| entry.transactions.len()).sum();
659 let txs = validate_and_hash_transactions(entries, num_txs, thread_pool, verify);
660 let Ok(txs) = txs else {
661 return false;
662 };
663 skip_verification || txs.unverified_signatures.verify().is_ok()
664 }
665
666 #[test]
667 fn test_entry_transaction_verify() {
668 let zero = Hash::default();
669
670 let keypair = Keypair::new();
672 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
673 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
674 let e0 = Entry::new(&zero, 0, vec![tx0, tx1]);
675 assert!(e0.verify(&zero));
676 let tx2 = system_transaction::transfer(&keypair, &keypair.pubkey(), 2, zero);
677 let tx3 = system_transaction::transfer(&keypair, &keypair.pubkey(), 3, zero);
678 let e1 = Entry::new(&zero, 0, vec![tx2, tx3]);
679 assert!(e1.verify(&zero));
680
681 let es = vec![e0, e1];
682 let thread_pool = ThreadPoolBuilder::new().build().unwrap();
683
684 let verify_transaction = {
686 move |versioned_tx: VersionedTransaction,
687 message_bytes: &[u8]|
688 -> Result<RuntimeTransaction<SanitizedTransaction>> {
689 RuntimeTransaction::try_create(
690 versioned_tx,
691 MessageHash::Precomputed(solana_message::VersionedMessage::hash_raw_message(
692 message_bytes,
693 )),
694 None,
695 SimpleAddressLoader::Disabled,
696 &ReservedAccountKeys::empty_key_set(),
697 true,
698 )
699 }
700 };
701
702 assert!(test_verify_transactions(
703 es,
704 false,
705 &thread_pool,
706 verify_transaction
707 ));
708 }
709
710 #[test]
711 fn test_validate_and_hash_transactions_and_signatures() {
712 let thread_pool = ThreadPoolBuilder::new().build().unwrap();
713 let zero = Hash::default();
714 let keypair = Keypair::new();
715 let tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
716 let entries = vec![Entry::new(&zero, 0, vec![tx])];
717
718 let validate_and_hash_transaction =
719 move |versioned_tx: VersionedTransaction,
720 message_bytes: &[u8]|
721 -> Result<RuntimeTransaction<SanitizedTransaction>> {
722 RuntimeTransaction::try_create(
723 versioned_tx,
724 MessageHash::Precomputed(solana_message::VersionedMessage::hash_raw_message(
725 message_bytes,
726 )),
727 None,
728 SimpleAddressLoader::Disabled,
729 &ReservedAccountKeys::empty_key_set(),
730 true,
731 )
732 };
733 let txs =
734 validate_and_hash_transactions(entries, 1, &thread_pool, validate_and_hash_transaction)
735 .expect("transaction validation and hashing must not verify signatures");
736 assert_eq!(txs.entries.len(), 1);
737 assert!(txs.unverified_signatures.verify().is_ok());
738
739 let mut tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
740 tx.signatures[0] = solana_signature::Signature::default();
741 let entries = vec![Entry::new(&zero, 0, vec![tx])];
742 let txs =
743 validate_and_hash_transactions(entries, 1, &thread_pool, validate_and_hash_transaction)
744 .expect("transaction validation and hashing must not verify signatures");
745 assert_eq!(txs.entries.len(), 1);
746 assert!(matches!(
747 txs.unverified_signatures.verify(),
748 Err(solana_transaction_error::TransactionError::SignatureFailure)
749 ));
750 }
751
752 #[test]
753 fn test_transaction_reorder_attack() {
754 let zero = Hash::default();
755
756 let keypair = Keypair::new();
758 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
759 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
760 let mut e0 = Entry::new(&zero, 0, vec![tx0.clone(), tx1.clone()]);
761 assert!(e0.verify(&zero));
762
763 e0.transactions[0] = tx1.into(); e0.transactions[1] = tx0.into();
766 assert!(!e0.verify(&zero));
767 }
768
769 #[test]
770 fn test_transaction_signing() {
771 let thread_pool = thread_pool_for_tests();
772
773 use solana_signature::Signature;
774 let zero = Hash::default();
775
776 let keypair = Keypair::new();
777 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
778 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
779
780 let mut e0 = [Entry::new(&zero, 0, vec![tx0, tx1])];
782 assert!(e0.verify(&zero, &thread_pool).status());
783
784 let orig_sig = e0[0].transactions[0].signatures[0];
786 e0[0].transactions[0].signatures[0] = Signature::default();
787 assert!(!e0.verify(&zero, &thread_pool).status());
788
789 e0[0].transactions[0].signatures[0] = orig_sig;
791 assert!(e0.verify(&zero, &thread_pool).status());
792
793 let len = e0[0].transactions[0].signatures.len();
795 e0[0].transactions[0]
796 .signatures
797 .resize(len - 1, Signature::default());
798 assert!(!e0.verify(&zero, &thread_pool).status());
799
800 let e0 = [Entry::new(&zero, 0, vec![])];
802 assert!(e0.verify(&zero, &thread_pool).status());
803 }
804
805 #[test]
806 fn test_next_entry() {
807 let zero = Hash::default();
808 let tick = next_entry(&zero, 1, vec![]);
809 assert_eq!(tick.num_hashes, 1);
810 assert_ne!(tick.hash, zero);
811
812 let tick = next_entry(&zero, 0, vec![]);
813 assert_eq!(tick.num_hashes, 0);
814 assert_eq!(tick.hash, zero);
815
816 let keypair = Keypair::new();
817 let tx0 = system_transaction::transfer(&keypair, &Pubkey::new_unique(), 42, zero);
818 let entry0 = next_entry(&zero, 1, vec![tx0.clone()]);
819 assert_eq!(entry0.num_hashes, 1);
820 assert_eq!(entry0.hash, next_hash(&zero, 1, &[tx0.into()]));
821 }
822
823 #[test]
824 #[should_panic]
825 fn test_next_entry_panic() {
826 let zero = Hash::default();
827 let keypair = Keypair::new();
828 let tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
829 next_entry(&zero, 0, vec![tx]);
830 }
831
832 #[test]
833 fn test_verify_slice1() {
834 agave_logger::setup();
835 let thread_pool = thread_pool_for_tests();
836
837 let zero = Hash::default();
838 let one = hash(zero.as_ref());
839 assert!(vec![][..].verify(&zero, &thread_pool).status());
841 assert!(
843 vec![Entry::new_tick(0, &zero)][..]
844 .verify(&zero, &thread_pool)
845 .status()
846 );
847 assert!(
849 !vec![Entry::new_tick(0, &zero)][..]
850 .verify(&one, &thread_pool)
851 .status()
852 );
853 assert!(
855 vec![next_entry(&zero, 0, vec![]); 2][..]
856 .verify(&zero, &thread_pool)
857 .status()
858 );
859
860 let mut bad_ticks = vec![next_entry(&zero, 0, vec![]); 2];
861 bad_ticks[1].hash = one;
862 assert!(!bad_ticks.verify(&zero, &thread_pool).status());
864 }
865
866 #[test]
867 fn test_verify_slice_with_hashes1() {
868 agave_logger::setup();
869 let thread_pool = thread_pool_for_tests();
870
871 let zero = Hash::default();
872 let one = hash(zero.as_ref());
873 let two = hash(one.as_ref());
874 assert!(vec![][..].verify(&one, &thread_pool).status());
876 assert!(
878 vec![Entry::new_tick(1, &two)][..]
879 .verify(&one, &thread_pool)
880 .status()
881 );
882 assert!(
884 !vec![Entry::new_tick(1, &two)][..]
885 .verify(&two, &thread_pool)
886 .status()
887 );
888
889 let mut ticks = vec![next_entry(&one, 1, vec![])];
890 ticks.push(next_entry(&ticks.last().unwrap().hash, 1, vec![]));
891 assert!(ticks.verify(&one, &thread_pool).status());
893
894 let mut bad_ticks = vec![next_entry(&one, 1, vec![])];
895 bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![]));
896 bad_ticks[1].hash = one;
897 assert!(!bad_ticks.verify(&one, &thread_pool).status());
899 }
900
901 #[test]
902 fn test_verify_slice_with_hashes_and_transactions() {
903 agave_logger::setup();
904 let thread_pool = thread_pool_for_tests();
905
906 let zero = Hash::default();
907 let one = hash(zero.as_ref());
908 let two = hash(one.as_ref());
909 let alice_keypair = Keypair::new();
910 let bob_keypair = Keypair::new();
911 let tx0 = system_transaction::transfer(&alice_keypair, &bob_keypair.pubkey(), 1, one);
912 let tx1 = system_transaction::transfer(&bob_keypair, &alice_keypair.pubkey(), 1, one);
913 assert!(vec![][..].verify(&one, &thread_pool).status());
915 assert!(
917 vec![next_entry(&one, 1, vec![tx0.clone()])][..]
918 .verify(&one, &thread_pool)
919 .status()
920 );
921 assert!(
923 !vec![next_entry(&one, 1, vec![tx0.clone()])][..]
924 .verify(&two, &thread_pool)
925 .status()
926 );
927
928 let mut ticks = vec![next_entry(&one, 1, vec![tx0.clone()])];
929 ticks.push(next_entry(
930 &ticks.last().unwrap().hash,
931 1,
932 vec![tx1.clone()],
933 ));
934
935 assert!(ticks.verify(&one, &thread_pool).status());
937
938 let mut bad_ticks = vec![next_entry(&one, 1, vec![tx0])];
939 bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![tx1]));
940 bad_ticks[1].hash = one;
941 assert!(!bad_ticks.verify(&one, &thread_pool).status());
943 }
944
945 #[test]
946 fn test_verify_tick_hash_count() {
947 let hashes_per_tick = 10;
948 let tx = VersionedTransaction::default();
949
950 let no_hash_tx_entry = Entry {
951 transactions: vec![tx.clone()],
952 ..Entry::default()
953 };
954 let single_hash_tx_entry = Entry {
955 transactions: vec![tx.clone()],
956 num_hashes: 1,
957 ..Entry::default()
958 };
959 let partial_tx_entry = Entry {
960 num_hashes: hashes_per_tick - 1,
961 transactions: vec![tx.clone()],
962 ..Entry::default()
963 };
964 let full_tx_entry = Entry {
965 num_hashes: hashes_per_tick,
966 transactions: vec![tx.clone()],
967 ..Entry::default()
968 };
969 let max_hash_tx_entry = Entry {
970 transactions: vec![tx],
971 num_hashes: u64::MAX,
972 ..Entry::default()
973 };
974
975 let no_hash_tick_entry = Entry::new_tick(0, &Hash::default());
976 let single_hash_tick_entry = Entry::new_tick(1, &Hash::default());
977 let partial_tick_entry = Entry::new_tick(hashes_per_tick - 1, &Hash::default());
978 let full_tick_entry = Entry::new_tick(hashes_per_tick, &Hash::default());
979 let max_hash_tick_entry = Entry::new_tick(u64::MAX, &Hash::default());
980
981 let mut tick_hash_count = 0;
983 let mut entries = vec![];
984 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
985 assert_eq!(tick_hash_count, 0);
986
987 tick_hash_count = hashes_per_tick;
989 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
990 assert_eq!(tick_hash_count, hashes_per_tick);
991 tick_hash_count = 0;
992
993 entries = vec![max_hash_tx_entry.clone()];
995 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, 0));
996 assert_eq!(tick_hash_count, 0);
997
998 entries = vec![partial_tick_entry.clone()];
1000 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1001 assert_eq!(tick_hash_count, hashes_per_tick - 1);
1002 tick_hash_count = 0;
1003
1004 entries = vec![no_hash_tx_entry, full_tick_entry.clone()];
1006 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1007 assert_eq!(tick_hash_count, 0);
1008
1009 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick - 1));
1011 assert_eq!(tick_hash_count, hashes_per_tick);
1012 tick_hash_count = 0;
1013
1014 entries = vec![partial_tx_entry];
1016 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1017 assert_eq!(tick_hash_count, hashes_per_tick - 1);
1018 tick_hash_count = 0;
1019
1020 entries = vec![no_hash_tick_entry.clone()];
1022 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1023 assert_eq!(tick_hash_count, 0);
1024
1025 entries = vec![full_tx_entry.clone(), no_hash_tick_entry];
1027 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1028 assert_eq!(tick_hash_count, hashes_per_tick);
1029 tick_hash_count = 0;
1030
1031 entries = vec![full_tx_entry.clone(), single_hash_tick_entry.clone()];
1033 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1034 assert_eq!(tick_hash_count, hashes_per_tick + 1);
1035 tick_hash_count = 0;
1036
1037 entries = vec![full_tx_entry];
1039 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1040 assert_eq!(tick_hash_count, hashes_per_tick);
1041 tick_hash_count = 0;
1042
1043 entries = vec![single_hash_tx_entry.clone(), partial_tick_entry];
1045 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1046 assert_eq!(tick_hash_count, 0);
1047
1048 let tx_entries: Vec<Entry> = (0..hashes_per_tick - 1)
1050 .map(|_| single_hash_tx_entry.clone())
1051 .collect();
1052 entries = [tx_entries, vec![single_hash_tick_entry]].concat();
1053 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1054 assert_eq!(tick_hash_count, 0);
1055
1056 entries = vec![full_tick_entry.clone(), max_hash_tick_entry];
1058 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1059 assert_eq!(tick_hash_count, u64::MAX);
1060 tick_hash_count = 0;
1061
1062 entries = vec![max_hash_tx_entry, full_tick_entry];
1064 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1065 assert_eq!(tick_hash_count, u64::MAX);
1066 }
1067
1068 #[test]
1069 fn test_poh_verify_fuzz() {
1070 agave_logger::setup();
1071 for _ in 0..100 {
1072 let mut time = Measure::start("ticks");
1073 let num_ticks = rng().random_range(1..100);
1074 info!("create {num_ticks} ticks:");
1075 let mut entries = create_random_ticks(num_ticks, 100, Hash::default());
1076 time.stop();
1077
1078 let mut modified = false;
1079 if rng().random_ratio(1, 2) {
1080 modified = true;
1081 let modify_idx = rng().random_range(0..num_ticks) as usize;
1082 entries[modify_idx].hash = hash(&[1, 2, 3]);
1083 }
1084
1085 info!("done.. {time}");
1086 let mut time = Measure::start("poh");
1087 let res = entries
1088 .verify(&Hash::default(), &thread_pool_for_tests())
1089 .status();
1090 assert_eq!(res, !modified);
1091 time.stop();
1092 info!("{time} {res}");
1093 }
1094 }
1095
1096 #[test]
1097 fn test_hash_transactions() {
1098 let mut transactions: Vec<_> = [test_tx(), test_tx(), test_tx()]
1099 .into_iter()
1100 .map(VersionedTransaction::from)
1101 .collect();
1102
1103 let hash1 = hash_transactions(&transactions);
1106 transactions.swap(0, 1);
1107 let hash2 = hash_transactions(&transactions);
1108 assert_ne!(hash1, hash2);
1109 }
1110
1111 #[test]
1112 fn test_deserialize_entries_rejects_txv1_unknown_config_mask_bit() {
1113 let tx = simple_v1_transaction_for_deserialization_tests();
1114 let entries = vec![next_versioned_entry(&Hash::default(), 1, vec![tx.clone()])];
1115 let mut serialized_entries = wincode::serialize(&entries).unwrap();
1116
1117 assert!(wincode::deserialize::<Vec<Entry>>(&serialized_entries).is_ok());
1118
1119 let serialized_tx = wincode::serialize(&tx).unwrap();
1120 let tx_offset = serialized_entries
1121 .windows(serialized_tx.len())
1122 .position(|window| window == serialized_tx)
1123 .expect("serialized transaction should be embedded in serialized entry");
1124
1125 const TXV1_CONFIG_MASK_OFFSET: usize = 1 + 3;
1127 let mask_offset = tx_offset + TXV1_CONFIG_MASK_OFFSET;
1128 let mask_range = mask_offset..mask_offset + core::mem::size_of::<u32>();
1129 let mask = u32::from_le_bytes(serialized_entries[mask_range.clone()].try_into().unwrap());
1130 assert_eq!(mask, 0);
1131
1132 let unknown_config_mask_bit = 1u32
1133 .checked_shl(v1::TransactionConfigMask::KNOWN_BITS.trailing_ones())
1134 .expect("txv1 config mask should have at least one unknown bit");
1135 assert_ne!(
1136 unknown_config_mask_bit & v1::TransactionConfigMask::KNOWN_BITS,
1137 unknown_config_mask_bit
1138 );
1139 serialized_entries[mask_range]
1140 .copy_from_slice(&(mask | unknown_config_mask_bit).to_le_bytes());
1141
1142 assert!(matches!(
1143 wincode::deserialize::<Vec<Entry>>(&serialized_entries),
1144 Err(wincode::ReadError::InvalidValue(
1145 "invalid transaction config mask"
1146 ))
1147 ));
1148 }
1149}