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(Debug)]
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 len(&self) -> usize {
194 self.signatures.len()
195 }
196
197 pub fn is_empty(&self) -> bool {
198 self.signatures.is_empty()
199 }
200
201 pub fn verify_signatures(&self, index: usize) -> bool {
202 let tx_data = &self.signatures[index];
203 tx_data
204 .signatures
205 .iter()
206 .zip(&tx_data.signer_pubkeys)
207 .all(|(signature, pubkey)| {
208 signature.verify(pubkey.as_ref(), &tx_data.serialized_message)
209 })
210 }
211
212 pub fn vote_transaction_message_hash(&self, index: usize) -> Option<Hash> {
213 let tx_data = &self.signatures[index];
214 (tx_data.is_simple_vote && !tx_data.signatures.is_empty()).then_some(tx_data.message_hash)
215 }
216
217 pub fn vote_transaction_message_hashes(&self) -> Vec<Hash> {
218 self.signatures
219 .iter()
220 .filter(|tx_signatures| tx_signatures.is_simple_vote)
221 .filter_map(|tx_signatures| {
222 tx_signatures
223 .signatures
224 .first()
225 .map(|_| tx_signatures.message_hash)
226 })
227 .collect()
228 }
229}
230
231pub struct ValidatedHashedTransactions<Tx: TransactionWithMeta> {
232 pub entries: Vec<EntryType<Tx>>,
233 pub unverified_signatures: UnverifiedSignatures,
234}
235
236impl Entry {
237 pub fn new(prev_hash: &Hash, mut num_hashes: u64, transactions: Vec<Transaction>) -> Self {
239 if num_hashes == 0 && !transactions.is_empty() {
242 num_hashes = 1;
243 }
244
245 let transactions = transactions.into_iter().map(Into::into).collect::<Vec<_>>();
246 let hash = next_hash(prev_hash, num_hashes, &transactions);
247 Entry {
248 num_hashes,
249 hash,
250 transactions,
251 }
252 }
253
254 pub fn new_mut(
255 start_hash: &mut Hash,
256 num_hashes: &mut u64,
257 transactions: Vec<Transaction>,
258 ) -> Self {
259 let entry = Self::new(start_hash, *num_hashes, transactions);
260 *start_hash = entry.hash;
261 *num_hashes = 0;
262
263 entry
264 }
265
266 #[cfg(test)]
267 pub fn new_tick(num_hashes: u64, hash: &Hash) -> Self {
268 Entry {
269 num_hashes,
270 hash: *hash,
271 transactions: vec![],
272 }
273 }
274
275 pub fn verify(&self, start_hash: &Hash) -> bool {
278 EntryVerificationData::from(self).verify(start_hash)
279 }
280
281 pub fn is_tick(&self) -> bool {
282 self.transactions.is_empty()
283 }
284}
285
286pub fn hash_signatures(signatures: &[impl AsRef<[u8]>]) -> Hash {
287 let merkle_tree = MerkleTree::new(signatures);
288 if let Some(root_hash) = merkle_tree.get_root() {
289 *root_hash
290 } else {
291 Hash::default()
292 }
293}
294
295pub fn hash_transactions(transactions: &[VersionedTransaction]) -> Hash {
296 let signatures: Vec<_> = transactions
298 .iter()
299 .flat_map(|tx| tx.signatures.iter())
300 .collect();
301 hash_signatures(&signatures)
302}
303
304fn next_hash_with_signatures(
305 start_hash: &Hash,
306 num_hashes: u64,
307 num_transactions: usize,
308 signatures: &[Signature],
309) -> Hash {
310 if num_hashes == 0 && num_transactions == 0 {
311 return *start_hash;
312 }
313
314 let mut poh = Poh::new(*start_hash, None);
315 poh.hash(num_hashes.saturating_sub(1));
316 if num_transactions == 0 {
317 poh.tick().unwrap().hash
318 } else {
319 poh.record(hash_signatures(signatures)).unwrap().hash
320 }
321}
322
323pub fn next_hash(
327 start_hash: &Hash,
328 num_hashes: u64,
329 transactions: &[VersionedTransaction],
330) -> Hash {
331 let signatures: Vec<_> = transactions
332 .iter()
333 .flat_map(|tx| tx.signatures.iter().copied())
334 .collect();
335 next_hash_with_signatures(start_hash, num_hashes, transactions.len(), &signatures)
336}
337
338pub struct EntryVerificationState {
339 verification_status: bool,
340 poh_duration_us: u64,
341}
342
343impl EntryVerificationState {
344 pub fn status(&self) -> bool {
345 self.verification_status
346 }
347
348 pub fn poh_duration_us(&self) -> u64 {
349 self.poh_duration_us
350 }
351}
352
353fn validate_and_hash_entry_transactions<Tx: TransactionWithMeta, F>(
354 entry: Entry,
355 verify: &F,
356 unverified_signatures: &mut UnverifiedSignatures,
357) -> Result<EntryType<Tx>>
358where
359 F: Fn(VersionedTransaction, &[u8]) -> Result<Tx>,
360{
361 if entry.transactions.is_empty() {
362 return Ok(EntryType::Tick(entry.hash));
363 }
364
365 let verified_transactions = entry
366 .transactions
367 .into_iter()
368 .map(|versioned_tx| {
369 let num_signers = usize::from(versioned_tx.message.header().num_required_signatures);
370 let static_account_keys = versioned_tx.message.static_account_keys();
371 if static_account_keys.len() < num_signers {
372 return Err(TransactionError::SanitizeFailure);
373 }
374 let signatures = versioned_tx.signatures.iter().copied().collect();
375 let signer_pubkeys = static_account_keys[..num_signers].iter().copied().collect();
376 let serialized_message = versioned_tx.message.serialize();
377 let verified_transaction = verify(versioned_tx, &serialized_message)?;
378 let message_hash = *verified_transaction.message_hash();
379 unverified_signatures.signatures.push(TxVerificationData {
380 is_simple_vote: verified_transaction.is_simple_vote_transaction(),
381 signatures,
382 message_hash,
383 serialized_message,
384 signer_pubkeys,
385 });
386
387 Ok(verified_transaction)
388 })
389 .collect::<Result<Vec<_>>>()?;
390 Ok(EntryType::Transactions(verified_transactions))
391}
392
393pub fn validate_and_hash_transactions<Tx: TransactionWithMeta + Send + Sync, F>(
398 entries: Vec<Entry>,
399 num_txs: usize,
400 thread_pool: &ThreadPool,
401 verify: F,
402) -> Result<ValidatedHashedTransactions<Tx>>
403where
404 F: Fn(VersionedTransaction, &[u8]) -> Result<Tx> + Send + Sync,
405{
406 const PARALLEL_VERIFY_THRESHOLD: usize = 200;
407 if num_txs < PARALLEL_VERIFY_THRESHOLD {
408 let mut unverified_signatures = UnverifiedSignatures::with_capacity(num_txs);
409 let entries = entries
410 .into_iter()
411 .map(|entry| {
412 validate_and_hash_entry_transactions(entry, &verify, &mut unverified_signatures)
413 })
414 .collect::<Result<_>>()?;
415 return Ok(ValidatedHashedTransactions {
416 entries,
417 unverified_signatures,
418 });
419 }
420
421 let verified = thread_pool.install(|| {
422 entries
423 .into_par_iter()
424 .map(|entry| {
425 let mut unverified_signatures =
426 UnverifiedSignatures::with_capacity(entry.transactions.len());
427 let verified_entry = validate_and_hash_entry_transactions(
428 entry,
429 &verify,
430 &mut unverified_signatures,
431 )?;
432 Ok((verified_entry, unverified_signatures))
433 })
434 .collect::<Result<Vec<_>>>()
435 })?;
436
437 let mut entries = Vec::with_capacity(verified.len());
438 let mut unverified_signatures = UnverifiedSignatures::with_capacity(num_txs);
439 for (entry, mut tx_unverified_signatures) in verified {
440 entries.push(entry);
441 unverified_signatures
442 .signatures
443 .append(&mut tx_unverified_signatures.signatures);
444 }
445 Ok(ValidatedHashedTransactions {
446 entries,
447 unverified_signatures,
448 })
449}
450
451pub fn verify_entries_cpu_in_pool(
452 entries: &[EntryVerificationData],
453 start_hash: &Hash,
454 thread_pool: &ThreadPool,
455) -> EntryVerificationState {
456 thread_pool.install(|| verify_entries_cpu(entries, start_hash))
457}
458
459fn verify_entries_cpu_generic(
460 entries: &[EntryVerificationData],
461 start_hash: &Hash,
462) -> EntryVerificationState {
463 let now = Instant::now();
464 let genesis = [EntryVerificationData {
465 num_hashes: 0,
466 hash: *start_hash,
467 num_transactions: 0,
468 signatures: Vec::new(),
469 }];
470 let entry_pairs = genesis.par_iter().chain(entries).zip(entries);
471 let res = entry_pairs.all(|(x0, x1)| {
472 let r = x1.verify(&x0.hash);
473 if !r {
474 warn!(
475 "entry invalid!: x0: {:?}, x1: {:?} num txs: {}",
476 x0.hash, x1.hash, x1.num_transactions
477 );
478 }
479 r
480 });
481 let poh_duration_us = now.elapsed().as_micros() as u64;
482 EntryVerificationState {
483 verification_status: res,
484 poh_duration_us,
485 }
486}
487
488pub fn verify_entries_cpu(
489 entries: &[EntryVerificationData],
490 start_hash: &Hash,
491) -> EntryVerificationState {
492 verify_entries_cpu_generic(entries, start_hash)
493}
494
495pub trait EntrySlice {
497 fn verify_cpu(&self, start_hash: &Hash) -> EntryVerificationState;
499 fn verify_cpu_generic(&self, start_hash: &Hash) -> EntryVerificationState;
500 fn verify(&self, start_hash: &Hash, thread_pool: &ThreadPool) -> EntryVerificationState;
501 fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool;
505 fn tick_count(&self) -> u64;
507}
508
509impl EntrySlice for [Entry] {
510 fn verify(&self, start_hash: &Hash, thread_pool: &ThreadPool) -> EntryVerificationState {
511 let verification_entries = entries_to_verification_data(self);
512 verify_entries_cpu_in_pool(&verification_entries, start_hash, thread_pool)
513 }
514
515 fn verify_cpu_generic(&self, start_hash: &Hash) -> EntryVerificationState {
516 let verification_entries = entries_to_verification_data(self);
517 verify_entries_cpu_generic(&verification_entries, start_hash)
518 }
519
520 fn verify_cpu(&self, start_hash: &Hash) -> EntryVerificationState {
521 let verification_entries = entries_to_verification_data(self);
522 verify_entries_cpu(&verification_entries, start_hash)
523 }
524
525 fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool {
526 if hashes_per_tick == 0 {
528 return true;
529 }
530
531 for entry in self {
532 *tick_hash_count = tick_hash_count.saturating_add(entry.num_hashes);
533 if entry.is_tick() {
534 if entry.num_hashes == 0 {
535 return false;
536 }
537 if *tick_hash_count != hashes_per_tick {
538 warn!(
539 "invalid tick hash count!: entry: {entry:#?}, tick_hash_count: \
540 {tick_hash_count}, hashes_per_tick: {hashes_per_tick}"
541 );
542 return false;
543 }
544 *tick_hash_count = 0;
545 }
546 }
547 *tick_hash_count < hashes_per_tick
548 }
549
550 fn tick_count(&self) -> u64 {
551 self.iter().filter(|e| e.is_tick()).count() as u64
552 }
553}
554
555pub fn next_entry_mut(start: &mut Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry {
556 let entry = Entry::new(start, num_hashes, transactions);
557 *start = entry.hash;
558 entry
559}
560
561pub fn create_ticks(num_ticks: u64, hashes_per_tick: u64, mut hash: Hash) -> Vec<Entry> {
562 repeat_with(|| next_entry_mut(&mut hash, hashes_per_tick, vec![]))
563 .take(num_ticks as usize)
564 .collect()
565}
566
567pub fn next_entry(prev_hash: &Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry {
569 let transactions = transactions.into_iter().map(Into::into).collect::<Vec<_>>();
570 next_versioned_entry(prev_hash, num_hashes, transactions)
571}
572
573pub fn next_versioned_entry(
575 prev_hash: &Hash,
576 num_hashes: u64,
577 transactions: Vec<VersionedTransaction>,
578) -> Entry {
579 assert!(num_hashes > 0 || transactions.is_empty());
580 Entry {
581 num_hashes,
582 hash: next_hash(prev_hash, num_hashes, &transactions),
583 transactions,
584 }
585}
586
587pub fn thread_pool_for_tests() -> ThreadPool {
588 rayon::ThreadPoolBuilder::new()
593 .num_threads(4)
594 .thread_name(|i| format!("solEntryTest{i:02}"))
595 .build()
596 .expect("new rayon threadpool")
597}
598
599#[cfg(feature = "dev-context-only-utils")]
600pub fn thread_pool_for_benches() -> ThreadPool {
601 rayon::ThreadPoolBuilder::new()
602 .num_threads(num_cpus::get())
603 .thread_name(|i| format!("solEntryBnch{i:02}"))
604 .build()
605 .expect("new rayon threadpool")
606}
607
608#[cfg(test)]
609mod tests {
610 use {
611 super::*,
612 agave_reserved_account_keys::ReservedAccountKeys,
613 rand::{Rng, rng},
614 rayon::ThreadPoolBuilder,
615 solana_hash::Hash,
616 solana_keypair::Keypair,
617 solana_measure::measure::Measure,
618 solana_message::{
619 MessageHeader, SimpleAddressLoader, VersionedMessage,
620 compiled_instruction::CompiledInstruction, v1,
621 },
622 solana_perf::test_tx::test_tx,
623 solana_pubkey::Pubkey,
624 solana_runtime_transaction::runtime_transaction::RuntimeTransaction,
625 solana_sha256_hasher::hash,
626 solana_signature::Signature,
627 solana_signer::Signer,
628 solana_system_transaction as system_transaction,
629 solana_transaction::{
630 sanitized::{MessageHash, SanitizedTransaction},
631 versioned::VersionedTransaction,
632 },
633 solana_transaction_error::TransactionResult as Result,
634 };
635
636 fn simple_v1_transaction_for_deserialization_tests() -> VersionedTransaction {
637 VersionedTransaction {
638 signatures: vec![Signature::default()],
639 message: VersionedMessage::V1(v1::Message {
640 header: MessageHeader {
641 num_required_signatures: 1,
642 num_readonly_signed_accounts: 0,
643 num_readonly_unsigned_accounts: 1,
644 },
645 config: v1::TransactionConfig::empty(),
646 lifetime_specifier: Hash::new_unique(),
647 account_keys: vec![Pubkey::new_unique(), Pubkey::new_unique()],
648 instructions: vec![CompiledInstruction {
649 program_id_index: 1,
650 accounts: vec![0],
651 data: vec![],
652 }],
653 }),
654 }
655 }
656
657 fn create_random_ticks(num_ticks: u64, max_hashes_per_tick: u64, mut hash: Hash) -> Vec<Entry> {
658 repeat_with(|| {
659 let hashes_per_tick = rng().random_range(1..max_hashes_per_tick);
660 next_entry_mut(&mut hash, hashes_per_tick, vec![])
661 })
662 .take(num_ticks as usize)
663 .collect()
664 }
665
666 #[test]
667 fn test_entry_verify() {
668 let zero = Hash::default();
669 let one = hash(zero.as_ref());
670 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)); }
675
676 fn test_verify_transactions<Tx: TransactionWithMeta + Send + Sync + 'static>(
677 entries: Vec<Entry>,
678 skip_verification: bool,
679 thread_pool: &ThreadPool,
680 verify: impl Fn(VersionedTransaction, &[u8]) -> Result<Tx> + Send + Sync,
681 ) -> bool {
682 let num_txs = entries.iter().map(|entry| entry.transactions.len()).sum();
683 let txs = validate_and_hash_transactions(entries, num_txs, thread_pool, verify);
684 let Ok(txs) = txs else {
685 return false;
686 };
687 skip_verification || txs.unverified_signatures.verify().is_ok()
688 }
689
690 #[test]
691 fn test_entry_transaction_verify() {
692 let zero = Hash::default();
693
694 let keypair = Keypair::new();
696 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
697 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
698 let e0 = Entry::new(&zero, 0, vec![tx0, tx1]);
699 assert!(e0.verify(&zero));
700 let tx2 = system_transaction::transfer(&keypair, &keypair.pubkey(), 2, zero);
701 let tx3 = system_transaction::transfer(&keypair, &keypair.pubkey(), 3, zero);
702 let e1 = Entry::new(&zero, 0, vec![tx2, tx3]);
703 assert!(e1.verify(&zero));
704
705 let es = vec![e0, e1];
706 let thread_pool = ThreadPoolBuilder::new().build().unwrap();
707
708 let verify_transaction = {
710 move |versioned_tx: VersionedTransaction,
711 message_bytes: &[u8]|
712 -> Result<RuntimeTransaction<SanitizedTransaction>> {
713 RuntimeTransaction::try_create(
714 versioned_tx,
715 MessageHash::Precomputed(solana_message::VersionedMessage::hash_raw_message(
716 message_bytes,
717 )),
718 None,
719 SimpleAddressLoader::Disabled,
720 &ReservedAccountKeys::empty_key_set(),
721 )
722 }
723 };
724
725 assert!(test_verify_transactions(
726 es,
727 false,
728 &thread_pool,
729 verify_transaction
730 ));
731 }
732
733 #[test]
734 fn test_validate_and_hash_transactions_and_signatures() {
735 let thread_pool = ThreadPoolBuilder::new().build().unwrap();
736 let zero = Hash::default();
737 let keypair = Keypair::new();
738 let tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
739 let entries = vec![Entry::new(&zero, 0, vec![tx])];
740
741 let validate_and_hash_transaction =
742 move |versioned_tx: VersionedTransaction,
743 message_bytes: &[u8]|
744 -> Result<RuntimeTransaction<SanitizedTransaction>> {
745 RuntimeTransaction::try_create(
746 versioned_tx,
747 MessageHash::Precomputed(solana_message::VersionedMessage::hash_raw_message(
748 message_bytes,
749 )),
750 None,
751 SimpleAddressLoader::Disabled,
752 &ReservedAccountKeys::empty_key_set(),
753 )
754 };
755 let txs =
756 validate_and_hash_transactions(entries, 1, &thread_pool, validate_and_hash_transaction)
757 .expect("transaction validation and hashing must not verify signatures");
758 assert_eq!(txs.entries.len(), 1);
759 assert!(txs.unverified_signatures.verify().is_ok());
760
761 let mut tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
762 tx.signatures[0] = solana_signature::Signature::default();
763 let entries = vec![Entry::new(&zero, 0, vec![tx])];
764 let txs =
765 validate_and_hash_transactions(entries, 1, &thread_pool, validate_and_hash_transaction)
766 .expect("transaction validation and hashing must not verify signatures");
767 assert_eq!(txs.entries.len(), 1);
768 assert!(matches!(
769 txs.unverified_signatures.verify(),
770 Err(solana_transaction_error::TransactionError::SignatureFailure)
771 ));
772 }
773
774 #[test]
775 fn test_transaction_reorder_attack() {
776 let zero = Hash::default();
777
778 let keypair = Keypair::new();
780 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
781 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
782 let mut e0 = Entry::new(&zero, 0, vec![tx0.clone(), tx1.clone()]);
783 assert!(e0.verify(&zero));
784
785 e0.transactions[0] = tx1.into(); e0.transactions[1] = tx0.into();
788 assert!(!e0.verify(&zero));
789 }
790
791 #[test]
792 fn test_transaction_signing() {
793 let thread_pool = thread_pool_for_tests();
794
795 use solana_signature::Signature;
796 let zero = Hash::default();
797
798 let keypair = Keypair::new();
799 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
800 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
801
802 let mut e0 = [Entry::new(&zero, 0, vec![tx0, tx1])];
804 assert!(e0.verify(&zero, &thread_pool).status());
805
806 let orig_sig = e0[0].transactions[0].signatures[0];
808 e0[0].transactions[0].signatures[0] = Signature::default();
809 assert!(!e0.verify(&zero, &thread_pool).status());
810
811 e0[0].transactions[0].signatures[0] = orig_sig;
813 assert!(e0.verify(&zero, &thread_pool).status());
814
815 let len = e0[0].transactions[0].signatures.len();
817 e0[0].transactions[0]
818 .signatures
819 .resize(len - 1, Signature::default());
820 assert!(!e0.verify(&zero, &thread_pool).status());
821
822 let e0 = [Entry::new(&zero, 0, vec![])];
824 assert!(e0.verify(&zero, &thread_pool).status());
825 }
826
827 #[test]
828 fn test_next_entry() {
829 let zero = Hash::default();
830 let tick = next_entry(&zero, 1, vec![]);
831 assert_eq!(tick.num_hashes, 1);
832 assert_ne!(tick.hash, zero);
833
834 let tick = next_entry(&zero, 0, vec![]);
835 assert_eq!(tick.num_hashes, 0);
836 assert_eq!(tick.hash, zero);
837
838 let keypair = Keypair::new();
839 let tx0 = system_transaction::transfer(&keypair, &Pubkey::new_unique(), 42, zero);
840 let entry0 = next_entry(&zero, 1, vec![tx0.clone()]);
841 assert_eq!(entry0.num_hashes, 1);
842 assert_eq!(entry0.hash, next_hash(&zero, 1, &[tx0.into()]));
843 }
844
845 #[test]
846 #[should_panic]
847 fn test_next_entry_panic() {
848 let zero = Hash::default();
849 let keypair = Keypair::new();
850 let tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
851 next_entry(&zero, 0, vec![tx]);
852 }
853
854 #[test]
855 fn test_verify_slice1() {
856 agave_logger::setup();
857 let thread_pool = thread_pool_for_tests();
858
859 let zero = Hash::default();
860 let one = hash(zero.as_ref());
861 assert!(vec![][..].verify(&zero, &thread_pool).status());
863 assert!(
865 vec![Entry::new_tick(0, &zero)][..]
866 .verify(&zero, &thread_pool)
867 .status()
868 );
869 assert!(
871 !vec![Entry::new_tick(0, &zero)][..]
872 .verify(&one, &thread_pool)
873 .status()
874 );
875 assert!(
877 vec![next_entry(&zero, 0, vec![]); 2][..]
878 .verify(&zero, &thread_pool)
879 .status()
880 );
881
882 let mut bad_ticks = vec![next_entry(&zero, 0, vec![]); 2];
883 bad_ticks[1].hash = one;
884 assert!(!bad_ticks.verify(&zero, &thread_pool).status());
886 }
887
888 #[test]
889 fn test_verify_slice_with_hashes1() {
890 agave_logger::setup();
891 let thread_pool = thread_pool_for_tests();
892
893 let zero = Hash::default();
894 let one = hash(zero.as_ref());
895 let two = hash(one.as_ref());
896 assert!(vec![][..].verify(&one, &thread_pool).status());
898 assert!(
900 vec![Entry::new_tick(1, &two)][..]
901 .verify(&one, &thread_pool)
902 .status()
903 );
904 assert!(
906 !vec![Entry::new_tick(1, &two)][..]
907 .verify(&two, &thread_pool)
908 .status()
909 );
910
911 let mut ticks = vec![next_entry(&one, 1, vec![])];
912 ticks.push(next_entry(&ticks.last().unwrap().hash, 1, vec![]));
913 assert!(ticks.verify(&one, &thread_pool).status());
915
916 let mut bad_ticks = vec![next_entry(&one, 1, vec![])];
917 bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![]));
918 bad_ticks[1].hash = one;
919 assert!(!bad_ticks.verify(&one, &thread_pool).status());
921 }
922
923 #[test]
924 fn test_verify_slice_with_hashes_and_transactions() {
925 agave_logger::setup();
926 let thread_pool = thread_pool_for_tests();
927
928 let zero = Hash::default();
929 let one = hash(zero.as_ref());
930 let two = hash(one.as_ref());
931 let alice_keypair = Keypair::new();
932 let bob_keypair = Keypair::new();
933 let tx0 = system_transaction::transfer(&alice_keypair, &bob_keypair.pubkey(), 1, one);
934 let tx1 = system_transaction::transfer(&bob_keypair, &alice_keypair.pubkey(), 1, one);
935 assert!(vec![][..].verify(&one, &thread_pool).status());
937 assert!(
939 vec![next_entry(&one, 1, vec![tx0.clone()])][..]
940 .verify(&one, &thread_pool)
941 .status()
942 );
943 assert!(
945 !vec![next_entry(&one, 1, vec![tx0.clone()])][..]
946 .verify(&two, &thread_pool)
947 .status()
948 );
949
950 let mut ticks = vec![next_entry(&one, 1, vec![tx0.clone()])];
951 ticks.push(next_entry(
952 &ticks.last().unwrap().hash,
953 1,
954 vec![tx1.clone()],
955 ));
956
957 assert!(ticks.verify(&one, &thread_pool).status());
959
960 let mut bad_ticks = vec![next_entry(&one, 1, vec![tx0])];
961 bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![tx1]));
962 bad_ticks[1].hash = one;
963 assert!(!bad_ticks.verify(&one, &thread_pool).status());
965 }
966
967 #[test]
968 fn test_verify_tick_hash_count() {
969 let hashes_per_tick = 10;
970 let tx = VersionedTransaction::default();
971
972 let no_hash_tx_entry = Entry {
973 transactions: vec![tx.clone()],
974 ..Entry::default()
975 };
976 let single_hash_tx_entry = Entry {
977 transactions: vec![tx.clone()],
978 num_hashes: 1,
979 ..Entry::default()
980 };
981 let partial_tx_entry = Entry {
982 num_hashes: hashes_per_tick - 1,
983 transactions: vec![tx.clone()],
984 ..Entry::default()
985 };
986 let full_tx_entry = Entry {
987 num_hashes: hashes_per_tick,
988 transactions: vec![tx.clone()],
989 ..Entry::default()
990 };
991 let max_hash_tx_entry = Entry {
992 transactions: vec![tx],
993 num_hashes: u64::MAX,
994 ..Entry::default()
995 };
996
997 let no_hash_tick_entry = Entry::new_tick(0, &Hash::default());
998 let single_hash_tick_entry = Entry::new_tick(1, &Hash::default());
999 let partial_tick_entry = Entry::new_tick(hashes_per_tick - 1, &Hash::default());
1000 let full_tick_entry = Entry::new_tick(hashes_per_tick, &Hash::default());
1001 let max_hash_tick_entry = Entry::new_tick(u64::MAX, &Hash::default());
1002
1003 let mut tick_hash_count = 0;
1005 let mut entries = vec![];
1006 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1007 assert_eq!(tick_hash_count, 0);
1008
1009 tick_hash_count = hashes_per_tick;
1011 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1012 assert_eq!(tick_hash_count, hashes_per_tick);
1013 tick_hash_count = 0;
1014
1015 entries = vec![max_hash_tx_entry.clone()];
1017 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, 0));
1018 assert_eq!(tick_hash_count, 0);
1019
1020 entries = vec![partial_tick_entry.clone()];
1022 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1023 assert_eq!(tick_hash_count, hashes_per_tick - 1);
1024 tick_hash_count = 0;
1025
1026 entries = vec![no_hash_tx_entry, full_tick_entry.clone()];
1028 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1029 assert_eq!(tick_hash_count, 0);
1030
1031 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick - 1));
1033 assert_eq!(tick_hash_count, hashes_per_tick);
1034 tick_hash_count = 0;
1035
1036 entries = vec![partial_tx_entry];
1038 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1039 assert_eq!(tick_hash_count, hashes_per_tick - 1);
1040 tick_hash_count = 0;
1041
1042 entries = vec![no_hash_tick_entry.clone()];
1044 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1045 assert_eq!(tick_hash_count, 0);
1046
1047 entries = vec![full_tx_entry.clone(), no_hash_tick_entry];
1049 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1050 assert_eq!(tick_hash_count, hashes_per_tick);
1051 tick_hash_count = 0;
1052
1053 entries = vec![full_tx_entry.clone(), single_hash_tick_entry.clone()];
1055 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1056 assert_eq!(tick_hash_count, hashes_per_tick + 1);
1057 tick_hash_count = 0;
1058
1059 entries = vec![full_tx_entry];
1061 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1062 assert_eq!(tick_hash_count, hashes_per_tick);
1063 tick_hash_count = 0;
1064
1065 entries = vec![single_hash_tx_entry.clone(), partial_tick_entry];
1067 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1068 assert_eq!(tick_hash_count, 0);
1069
1070 let tx_entries: Vec<Entry> = (0..hashes_per_tick - 1)
1072 .map(|_| single_hash_tx_entry.clone())
1073 .collect();
1074 entries = [tx_entries, vec![single_hash_tick_entry]].concat();
1075 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1076 assert_eq!(tick_hash_count, 0);
1077
1078 entries = vec![full_tick_entry.clone(), max_hash_tick_entry];
1080 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1081 assert_eq!(tick_hash_count, u64::MAX);
1082 tick_hash_count = 0;
1083
1084 entries = vec![max_hash_tx_entry, full_tick_entry];
1086 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1087 assert_eq!(tick_hash_count, u64::MAX);
1088 }
1089
1090 #[test]
1091 fn test_poh_verify_fuzz() {
1092 agave_logger::setup();
1093 for _ in 0..100 {
1094 let mut time = Measure::start("ticks");
1095 let num_ticks = rng().random_range(1..100);
1096 info!("create {num_ticks} ticks:");
1097 let mut entries = create_random_ticks(num_ticks, 100, Hash::default());
1098 time.stop();
1099
1100 let mut modified = false;
1101 if rng().random_ratio(1, 2) {
1102 modified = true;
1103 let modify_idx = rng().random_range(0..num_ticks) as usize;
1104 entries[modify_idx].hash = hash(&[1, 2, 3]);
1105 }
1106
1107 info!("done.. {time}");
1108 let mut time = Measure::start("poh");
1109 let res = entries
1110 .verify(&Hash::default(), &thread_pool_for_tests())
1111 .status();
1112 assert_eq!(res, !modified);
1113 time.stop();
1114 info!("{time} {res}");
1115 }
1116 }
1117
1118 #[test]
1119 fn test_hash_transactions() {
1120 let mut transactions: Vec<_> = [test_tx(), test_tx(), test_tx()]
1121 .into_iter()
1122 .map(VersionedTransaction::from)
1123 .collect();
1124
1125 let hash1 = hash_transactions(&transactions);
1128 transactions.swap(0, 1);
1129 let hash2 = hash_transactions(&transactions);
1130 assert_ne!(hash1, hash2);
1131 }
1132
1133 #[test]
1134 fn test_deserialize_entries_rejects_txv1_unknown_config_mask_bit() {
1135 let tx = simple_v1_transaction_for_deserialization_tests();
1136 let entries = vec![next_versioned_entry(&Hash::default(), 1, vec![tx.clone()])];
1137 let mut serialized_entries = wincode::serialize(&entries).unwrap();
1138
1139 assert!(wincode::deserialize::<Vec<Entry>>(&serialized_entries).is_ok());
1140
1141 let serialized_tx = wincode::serialize(&tx).unwrap();
1142 let tx_offset = serialized_entries
1143 .windows(serialized_tx.len())
1144 .position(|window| window == serialized_tx)
1145 .expect("serialized transaction should be embedded in serialized entry");
1146
1147 const TXV1_CONFIG_MASK_OFFSET: usize = 1 + 3;
1149 let mask_offset = tx_offset + TXV1_CONFIG_MASK_OFFSET;
1150 let mask_range = mask_offset..mask_offset + core::mem::size_of::<u32>();
1151 let mask = u32::from_le_bytes(serialized_entries[mask_range.clone()].try_into().unwrap());
1152 assert_eq!(mask, 0);
1153
1154 let unknown_config_mask_bit = 1u32
1155 .checked_shl(v1::TransactionConfigMask::KNOWN_BITS.trailing_ones())
1156 .expect("txv1 config mask should have at least one unknown bit");
1157 assert_ne!(
1158 unknown_config_mask_bit & v1::TransactionConfigMask::KNOWN_BITS,
1159 unknown_config_mask_bit
1160 );
1161 serialized_entries[mask_range]
1162 .copy_from_slice(&(mask | unknown_config_mask_bit).to_le_bytes());
1163
1164 assert!(matches!(
1165 wincode::deserialize::<Vec<Entry>>(&serialized_entries),
1166 Err(wincode::ReadError::InvalidValue(
1167 "invalid transaction config mask"
1168 ))
1169 ));
1170 }
1171}