1use {
6 crate::poh::Poh,
7 agave_transaction_view::{
8 transaction_data::TransactionData, transaction_view::UnsanitizedTransactionView,
9 },
10 crossbeam_channel::{Receiver, Sender},
11 log::*,
12 rayon::{ThreadPool, prelude::*},
13 smallvec::SmallVec,
14 solana_address::Address,
15 solana_cost_model::shred_limit::DEFAULT_MAX_DATA_SHREDS_PER_SLOT,
16 solana_hash::Hash,
17 solana_merkle_tree::MerkleTree,
18 solana_runtime_transaction::transaction_with_meta::TransactionWithMeta,
19 solana_signature::Signature,
20 solana_transaction::{Transaction, versioned::VersionedTransaction},
21 solana_transaction_error::{TransactionError, TransactionResult as Result},
22 std::{fmt::Formatter, iter::repeat_with, time::Instant},
23 wincode::{SchemaRead, SchemaWrite, containers::Vec as WincodeVec, len::BincodeLen},
24};
25
26pub type EntrySender = Sender<Vec<Entry>>;
27pub type EntryReceiver = Receiver<Vec<Entry>>;
28
29pub const MAX_DATA_SHREDS_SIZE: usize =
30 DEFAULT_MAX_DATA_SHREDS_PER_SLOT as usize * solana_packet::PACKET_DATA_SIZE;
31pub type MaxDataShredsLen = BincodeLen<MAX_DATA_SHREDS_SIZE>;
32
33#[derive(Debug, Default, PartialEq, Eq, Clone, SchemaWrite, SchemaRead)]
48pub struct Entry {
49 pub num_hashes: u64,
51
52 pub hash: Hash,
54
55 #[wincode(with = "WincodeVec<VersionedTransaction, MaxDataShredsLen>")]
59 pub transactions: Vec<VersionedTransaction>,
60}
61
62pub struct EntryView<D: TransactionData> {
63 pub num_hashes: u64,
65
66 pub hash: Hash,
68
69 pub transactions: Vec<UnsanitizedTransactionView<D>>,
73}
74
75#[cfg(feature = "dev-context-only-utils")]
77impl<D> PartialEq for EntryView<D>
78where
79 D: PartialEq + TransactionData,
80{
81 fn eq(&self, other: &Self) -> bool {
82 let same_num_hashes = self.num_hashes == other.num_hashes;
83 let same_hash = self.hash == other.hash;
84 let same_transactions = || {
85 let same_len = self.transactions.len() == other.transactions.len();
86
87 if !same_len {
88 return false;
89 }
90
91 self.transactions
92 .iter()
93 .zip(other.transactions.iter())
94 .all(|(tx1, tx2)| tx1.data() == tx2.data())
95 };
96
97 same_num_hashes && same_hash && same_transactions()
98 }
99}
100
101impl<D> std::fmt::Debug for EntryView<D>
102where
103 D: TransactionData,
104{
105 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
106 f.debug_struct("EntryView")
107 .field("num_hashes", &self.num_hashes)
108 .field("hash", &self.hash)
109 .field("transactions", &self.transactions)
110 .finish()
111 }
112}
113
114impl<D: TransactionData> EntryView<D> {
115 pub fn is_tick(&self) -> bool {
116 self.transactions.is_empty()
117 }
118}
119
120#[cfg(feature = "dev-context-only-utils")]
124pub fn entry_views_for_tests(entries: Vec<Entry>) -> Vec<EntryView<bytes::Bytes>> {
125 let component = crate::block_component::BlockComponent::new_entry_batch(entries).unwrap();
126 let bytes = wincode::serialize(&component).unwrap();
127 let crate::block_component::ParsedBlockComponent::EntryBatch(views) =
128 crate::block_component_parser::parse(bytes).unwrap()
129 else {
130 panic!("expected EntryBatch");
131 };
132 views
133}
134
135#[derive(Clone, Debug, Default, PartialEq, Eq)]
137pub struct EntryVerificationData {
138 pub num_hashes: u64,
139 pub hash: Hash,
140 pub num_transactions: usize,
141 pub signatures: Vec<Signature>,
142}
143
144impl From<&Entry> for EntryVerificationData {
145 fn from(entry: &Entry) -> Self {
146 Self {
147 num_hashes: entry.num_hashes,
148 hash: entry.hash,
149 num_transactions: entry.transactions.len(),
150 signatures: entry
151 .transactions
152 .iter()
153 .flat_map(|tx| tx.signatures.iter().copied())
154 .collect(),
155 }
156 }
157}
158
159impl<D: TransactionData> From<&EntryView<D>> for EntryVerificationData {
160 fn from(entry: &EntryView<D>) -> Self {
161 Self {
162 num_hashes: entry.num_hashes,
163 hash: entry.hash,
164 num_transactions: entry.transactions.len(),
165 signatures: entry
166 .transactions
167 .iter()
168 .flat_map(|tx| tx.signatures().iter().copied())
169 .collect(),
170 }
171 }
172}
173
174impl EntryVerificationData {
175 pub fn verify(&self, start_hash: &Hash) -> bool {
176 let ref_hash = next_hash_with_signatures(
177 start_hash,
178 self.num_hashes,
179 self.num_transactions,
180 &self.signatures,
181 );
182 if self.hash != ref_hash {
183 warn!(
184 "next_hash is invalid expected: {:?} actual: {:?}",
185 self.hash, ref_hash
186 );
187 return false;
188 }
189 true
190 }
191}
192
193pub fn entries_to_verification_data(entries: &[Entry]) -> Vec<EntryVerificationData> {
194 entries.iter().map(Into::into).collect()
195}
196
197pub fn entry_views_to_verification_data<D: TransactionData>(
198 entries: &[EntryView<D>],
199) -> Vec<EntryVerificationData> {
200 entries.iter().map(Into::into).collect()
201}
202
203pub struct EntrySummary {
204 pub num_hashes: u64,
205 pub hash: Hash,
206 pub num_transactions: u64,
207}
208
209impl From<&Entry> for EntrySummary {
210 fn from(entry: &Entry) -> Self {
211 Self {
212 num_hashes: entry.num_hashes,
213 hash: entry.hash,
214 num_transactions: entry.transactions.len() as u64,
215 }
216 }
217}
218
219impl<D: TransactionData> From<&EntryView<D>> for EntrySummary {
220 fn from(entry: &EntryView<D>) -> Self {
221 Self {
222 num_hashes: entry.num_hashes,
223 hash: entry.hash,
224 num_transactions: entry.transactions.len() as u64,
225 }
226 }
227}
228
229pub enum EntryType<Tx: TransactionWithMeta> {
231 Transactions(Vec<Tx>),
232 Tick(Hash),
233}
234
235#[derive(Debug)]
236struct TxVerificationData<D: TransactionData> {
237 is_simple_vote: bool,
238 signatures: SmallVec<[Signature; 2]>,
239 signer_pubkeys: SmallVec<[Address; 2]>,
240 message_hash: Hash,
241 transaction_view: UnsanitizedTransactionView<D>,
242}
243
244pub struct UnverifiedSignatures<D: TransactionData> {
245 signatures: Vec<TxVerificationData<D>>,
246}
247
248impl<D> UnverifiedSignatures<D>
249where
250 D: TransactionData + Send + Sync,
251{
252 fn with_capacity(capacity: usize) -> Self {
253 Self {
254 signatures: Vec::with_capacity(capacity),
255 }
256 }
257
258 pub fn verify(&self) -> Result<()> {
261 let all_valid = self
262 .signatures
263 .par_iter()
264 .flat_map_iter(|tx| {
265 let message = tx.transaction_view.message_data();
266 tx.signatures
267 .iter()
268 .zip(tx.signer_pubkeys.iter())
269 .map(move |(signature, pubkey)| (signature, pubkey, message))
270 })
271 .all(|(signature, pubkey, message)| signature.verify(pubkey.as_ref(), message));
272
273 if all_valid {
274 Ok(())
275 } else {
276 Err(TransactionError::SignatureFailure)
277 }
278 }
279
280 #[cfg(feature = "dev-context-only-utils")]
281 pub fn verify_single_loop_for_benches(&self) -> Result<()> {
284 self.signatures.par_iter().try_for_each(|tx_signatures| {
285 if tx_signatures
286 .signatures
287 .iter()
288 .zip(tx_signatures.signer_pubkeys.iter())
289 .all(|(signature, pubkey)| {
290 signature.verify(
291 pubkey.as_ref(),
292 tx_signatures.transaction_view.message_data(),
293 )
294 })
295 {
296 Ok(())
297 } else {
298 Err(TransactionError::SignatureFailure)
299 }
300 })
301 }
302
303 pub fn len(&self) -> usize {
304 self.signatures.len()
305 }
306
307 pub fn is_empty(&self) -> bool {
308 self.signatures.is_empty()
309 }
310
311 pub fn verify_signatures(&self, index: usize) -> bool {
312 let tx_data = &self.signatures[index];
313 let message = tx_data.transaction_view.message_data();
314 tx_data
315 .signatures
316 .iter()
317 .zip(&tx_data.signer_pubkeys)
318 .all(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message))
319 }
320
321 pub fn vote_transaction_message_hash(&self, index: usize) -> Option<Hash> {
322 let tx_data = &self.signatures[index];
323 (tx_data.is_simple_vote && !tx_data.signatures.is_empty()).then_some(tx_data.message_hash)
324 }
325
326 pub fn vote_transaction_message_hashes(&self) -> Vec<Hash> {
327 self.signatures
328 .iter()
329 .filter(|tx_signatures| tx_signatures.is_simple_vote)
330 .filter_map(|tx_signatures| {
331 tx_signatures
332 .signatures
333 .first()
334 .map(|_| tx_signatures.message_hash)
335 })
336 .collect()
337 }
338}
339
340pub struct ValidatedHashedTransactions<Tx: TransactionWithMeta, D: TransactionData> {
341 pub entries: Vec<EntryType<Tx>>,
342 pub unverified_signatures: UnverifiedSignatures<D>,
343}
344
345impl Entry {
346 pub fn new(prev_hash: &Hash, mut num_hashes: u64, transactions: Vec<Transaction>) -> Self {
348 if num_hashes == 0 && !transactions.is_empty() {
351 num_hashes = 1;
352 }
353
354 let transactions = transactions.into_iter().map(Into::into).collect::<Vec<_>>();
355 let hash = next_hash(prev_hash, num_hashes, &transactions);
356 Entry {
357 num_hashes,
358 hash,
359 transactions,
360 }
361 }
362
363 pub fn new_mut(
364 start_hash: &mut Hash,
365 num_hashes: &mut u64,
366 transactions: Vec<Transaction>,
367 ) -> Self {
368 let entry = Self::new(start_hash, *num_hashes, transactions);
369 *start_hash = entry.hash;
370 *num_hashes = 0;
371
372 entry
373 }
374
375 #[cfg(test)]
376 pub fn new_tick(num_hashes: u64, hash: &Hash) -> Self {
377 Entry {
378 num_hashes,
379 hash: *hash,
380 transactions: vec![],
381 }
382 }
383
384 pub fn verify(&self, start_hash: &Hash) -> bool {
387 EntryVerificationData::from(self).verify(start_hash)
388 }
389
390 pub fn is_tick(&self) -> bool {
391 self.transactions.is_empty()
392 }
393}
394
395pub fn hash_signatures(signatures: &[impl AsRef<[u8]>]) -> Hash {
396 let merkle_tree = MerkleTree::new(signatures);
397 if let Some(root_hash) = merkle_tree.get_root() {
398 *root_hash
399 } else {
400 Hash::default()
401 }
402}
403
404pub fn hash_transactions(transactions: &[VersionedTransaction]) -> Hash {
405 let signatures: Vec<_> = transactions
407 .iter()
408 .flat_map(|tx| tx.signatures.iter())
409 .collect();
410 hash_signatures(&signatures)
411}
412
413fn next_hash_with_signatures(
414 start_hash: &Hash,
415 num_hashes: u64,
416 num_transactions: usize,
417 signatures: &[Signature],
418) -> Hash {
419 if num_hashes == 0 && num_transactions == 0 {
420 return *start_hash;
421 }
422
423 let mut poh = Poh::new(*start_hash, None);
424 poh.hash(num_hashes.saturating_sub(1));
425 if num_transactions == 0 {
426 poh.tick().unwrap().hash
427 } else {
428 poh.record(hash_signatures(signatures)).unwrap().hash
429 }
430}
431
432pub fn next_hash(
436 start_hash: &Hash,
437 num_hashes: u64,
438 transactions: &[VersionedTransaction],
439) -> Hash {
440 let signatures: Vec<_> = transactions
441 .iter()
442 .flat_map(|tx| tx.signatures.iter().copied())
443 .collect();
444 next_hash_with_signatures(start_hash, num_hashes, transactions.len(), &signatures)
445}
446
447pub struct EntryVerificationState {
448 verification_status: bool,
449 poh_duration_us: u64,
450}
451
452impl EntryVerificationState {
453 pub fn status(&self) -> bool {
454 self.verification_status
455 }
456
457 pub fn poh_duration_us(&self) -> u64 {
458 self.poh_duration_us
459 }
460}
461
462fn validate_and_hash_entry_transactions<Tx: TransactionWithMeta, F, D>(
463 entry: EntryView<D>,
464 verify: &F,
465 unverified_signatures: &mut UnverifiedSignatures<D>,
466) -> Result<EntryType<Tx>>
467where
468 F: Fn(UnsanitizedTransactionView<D>) -> Result<Tx>,
469 D: TransactionData + Clone,
470{
471 if entry.transactions.is_empty() {
472 return Ok(EntryType::Tick(entry.hash));
473 }
474
475 let verified_transactions = entry
476 .transactions
477 .into_iter()
478 .map(|transaction_view| {
479 let num_signers: usize = transaction_view.num_required_signatures().into();
480 let static_account_keys = transaction_view.static_account_keys();
481 if static_account_keys.len() < num_signers {
482 return Err(TransactionError::SanitizeFailure);
483 }
484 let signatures = transaction_view.signatures().iter().copied().collect();
485 let signer_pubkeys = static_account_keys[..num_signers].iter().copied().collect();
486 let verified_transaction = verify(transaction_view.clone())?;
487 let message_hash = *verified_transaction.message_hash();
488 unverified_signatures.signatures.push(TxVerificationData {
489 is_simple_vote: verified_transaction.is_simple_vote_transaction(),
490 signatures,
491 message_hash,
492 transaction_view,
493 signer_pubkeys,
494 });
495
496 Ok(verified_transaction)
497 })
498 .collect::<Result<Vec<_>>>()?;
499 Ok(EntryType::Transactions(verified_transactions))
500}
501
502pub fn validate_and_hash_transactions<Tx, F, D>(
507 entries: Vec<EntryView<D>>,
508 num_txs: usize,
509 thread_pool: &ThreadPool,
510 verify: F,
511) -> Result<ValidatedHashedTransactions<Tx, D>>
512where
513 Tx: TransactionWithMeta + Send + Sync,
514 F: Fn(UnsanitizedTransactionView<D>) -> Result<Tx> + Send + Sync,
515 D: TransactionData + Clone + Send + Sync,
516{
517 const PARALLEL_VERIFY_THRESHOLD: usize = 200;
518 if num_txs < PARALLEL_VERIFY_THRESHOLD {
519 let mut unverified_signatures = UnverifiedSignatures::with_capacity(num_txs);
520 let entries = entries
521 .into_iter()
522 .map(|entry| {
523 validate_and_hash_entry_transactions(entry, &verify, &mut unverified_signatures)
524 })
525 .collect::<Result<_>>()?;
526 return Ok(ValidatedHashedTransactions {
527 entries,
528 unverified_signatures,
529 });
530 }
531
532 let verified = thread_pool.install(|| {
533 entries
534 .into_par_iter()
535 .map(|entry| {
536 let mut unverified_signatures =
537 UnverifiedSignatures::with_capacity(entry.transactions.len());
538 let verified_entry = validate_and_hash_entry_transactions(
539 entry,
540 &verify,
541 &mut unverified_signatures,
542 )?;
543 Ok((verified_entry, unverified_signatures))
544 })
545 .collect::<Result<Vec<_>>>()
546 })?;
547
548 let mut entries = Vec::with_capacity(verified.len());
549 let mut unverified_signatures = UnverifiedSignatures::with_capacity(num_txs);
550 for (entry, mut tx_unverified_signatures) in verified {
551 entries.push(entry);
552 unverified_signatures
553 .signatures
554 .append(&mut tx_unverified_signatures.signatures);
555 }
556 Ok(ValidatedHashedTransactions {
557 entries,
558 unverified_signatures,
559 })
560}
561
562pub fn verify_entries_cpu_in_pool(
563 entries: &[EntryVerificationData],
564 start_hash: &Hash,
565 thread_pool: &ThreadPool,
566) -> EntryVerificationState {
567 thread_pool.install(|| verify_entries_cpu(entries, start_hash))
568}
569
570fn verify_entries_cpu_generic(
571 entries: &[EntryVerificationData],
572 start_hash: &Hash,
573) -> EntryVerificationState {
574 let now = Instant::now();
575 let genesis = [EntryVerificationData {
576 num_hashes: 0,
577 hash: *start_hash,
578 num_transactions: 0,
579 signatures: Vec::new(),
580 }];
581 let entry_pairs = genesis.par_iter().chain(entries).zip(entries);
582 let res = entry_pairs.all(|(x0, x1)| {
583 let r = x1.verify(&x0.hash);
584 if !r {
585 warn!(
586 "entry invalid!: x0: {:?}, x1: {:?} num txs: {}",
587 x0.hash, x1.hash, x1.num_transactions
588 );
589 }
590 r
591 });
592 let poh_duration_us = now.elapsed().as_micros() as u64;
593 EntryVerificationState {
594 verification_status: res,
595 poh_duration_us,
596 }
597}
598
599pub fn verify_entries_cpu(
600 entries: &[EntryVerificationData],
601 start_hash: &Hash,
602) -> EntryVerificationState {
603 verify_entries_cpu_generic(entries, start_hash)
604}
605
606pub trait EntrySlice {
608 fn verify_cpu(&self, start_hash: &Hash) -> EntryVerificationState;
610 fn verify_cpu_generic(&self, start_hash: &Hash) -> EntryVerificationState;
611 fn verify(&self, start_hash: &Hash, thread_pool: &ThreadPool) -> EntryVerificationState;
612}
613
614impl EntrySlice for [Entry] {
615 fn verify(&self, start_hash: &Hash, thread_pool: &ThreadPool) -> EntryVerificationState {
616 let verification_entries = entries_to_verification_data(self);
617 verify_entries_cpu_in_pool(&verification_entries, start_hash, thread_pool)
618 }
619
620 fn verify_cpu_generic(&self, start_hash: &Hash) -> EntryVerificationState {
621 let verification_entries = entries_to_verification_data(self);
622 verify_entries_cpu_generic(&verification_entries, start_hash)
623 }
624
625 fn verify_cpu(&self, start_hash: &Hash) -> EntryVerificationState {
626 let verification_entries = entries_to_verification_data(self);
627 verify_entries_cpu(&verification_entries, start_hash)
628 }
629}
630
631impl EntrySliceTickCheck for [Entry] {
632 fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool {
633 if hashes_per_tick == 0 {
635 return true;
636 }
637
638 for entry in self {
639 *tick_hash_count = tick_hash_count.saturating_add(entry.num_hashes);
640 if entry.is_tick() {
641 if entry.num_hashes == 0 {
642 return false;
643 }
644 if *tick_hash_count != hashes_per_tick {
645 warn!(
646 "invalid tick hash count!: entry: {entry:#?}, tick_hash_count: \
647 {tick_hash_count}, hashes_per_tick: {hashes_per_tick}"
648 );
649 return false;
650 }
651 *tick_hash_count = 0;
652 }
653 }
654 *tick_hash_count < hashes_per_tick
655 }
656
657 fn tick_count(&self) -> u64 {
658 self.iter().filter(|e| e.is_tick()).count() as u64
659 }
660}
661pub trait EntrySliceTickCheck {
662 fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool;
666
667 fn tick_count(&self) -> u64;
669}
670
671impl<D: TransactionData> EntrySliceTickCheck for [EntryView<D>] {
672 fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool {
673 if hashes_per_tick == 0 {
675 return true;
676 }
677
678 for entry in self {
679 *tick_hash_count = tick_hash_count.saturating_add(entry.num_hashes);
680 if entry.is_tick() {
681 if entry.num_hashes == 0 {
682 return false;
683 }
684 if *tick_hash_count != hashes_per_tick {
685 warn!(
686 "invalid tick hash count!: entry: {entry:#?}, tick_hash_count: \
687 {tick_hash_count}, hashes_per_tick: {hashes_per_tick}"
688 );
689 return false;
690 }
691 *tick_hash_count = 0;
692 }
693 }
694 *tick_hash_count < hashes_per_tick
695 }
696
697 fn tick_count(&self) -> u64 {
698 self.iter().filter(|e| e.is_tick()).count() as u64
699 }
700}
701
702pub fn next_entry_mut(start: &mut Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry {
703 let entry = Entry::new(start, num_hashes, transactions);
704 *start = entry.hash;
705 entry
706}
707
708pub fn create_ticks(num_ticks: u64, hashes_per_tick: u64, mut hash: Hash) -> Vec<Entry> {
709 repeat_with(|| next_entry_mut(&mut hash, hashes_per_tick, vec![]))
710 .take(num_ticks as usize)
711 .collect()
712}
713
714pub fn next_entry(prev_hash: &Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry {
716 let transactions = transactions.into_iter().map(Into::into).collect::<Vec<_>>();
717 next_versioned_entry(prev_hash, num_hashes, transactions)
718}
719
720pub fn next_versioned_entry(
722 prev_hash: &Hash,
723 num_hashes: u64,
724 transactions: Vec<VersionedTransaction>,
725) -> Entry {
726 assert!(num_hashes > 0 || transactions.is_empty());
727 Entry {
728 num_hashes,
729 hash: next_hash(prev_hash, num_hashes, &transactions),
730 transactions,
731 }
732}
733
734pub fn thread_pool_for_tests() -> ThreadPool {
735 rayon::ThreadPoolBuilder::new()
740 .num_threads(4)
741 .thread_name(|i| format!("solEntryTest{i:02}"))
742 .build()
743 .expect("new rayon threadpool")
744}
745
746#[cfg(feature = "dev-context-only-utils")]
747pub fn thread_pool_for_benches() -> ThreadPool {
748 rayon::ThreadPoolBuilder::new()
749 .num_threads(num_cpus::get())
750 .thread_name(|i| format!("solEntryBnch{i:02}"))
751 .build()
752 .expect("new rayon threadpool")
753}
754
755#[cfg(test)]
756mod tests {
757 use {
758 super::*,
759 crate::block_component::{BlockComponent, ParsedBlockComponent},
760 agave_reserved_account_keys::ReservedAccountKeys,
761 agave_transaction_view::transaction_view::SanitizedTransactionView,
762 bytes::Bytes,
763 rand::{Rng, rng},
764 rayon::ThreadPoolBuilder,
765 solana_hash::Hash,
766 solana_keypair::Keypair,
767 solana_measure::measure::Measure,
768 solana_message::{
769 MessageHeader, VersionedMessage, compiled_instruction::CompiledInstruction, v1,
770 },
771 solana_perf::test_tx::test_tx,
772 solana_pubkey::Pubkey,
773 solana_runtime_transaction::{
774 runtime_transaction::{ReplayTransaction, RuntimeTransaction},
775 sanitize_config::sanitize_config,
776 },
777 solana_sha256_hasher::hash,
778 solana_signature::Signature,
779 solana_signer::Signer,
780 solana_system_transaction as system_transaction,
781 solana_transaction::{sanitized::MessageHash, versioned::VersionedTransaction},
782 solana_transaction_error::TransactionResult as Result,
783 };
784
785 fn entry_view_from_entry(entry: Entry) -> EntryView<Bytes> {
786 let component = BlockComponent::new_entry_batch(vec![entry]).unwrap();
787 let bytes = wincode::serialize(&component).unwrap();
788 let ParsedBlockComponent::EntryBatch(mut views) =
789 crate::block_component_parser::parse(bytes).unwrap()
790 else {
791 panic!("expected EntryBatch");
792 };
793 views.pop().unwrap()
794 }
795
796 fn simple_v1_transaction_for_deserialization_tests() -> VersionedTransaction {
797 VersionedTransaction {
798 signatures: vec![Signature::default()],
799 message: VersionedMessage::V1(v1::Message {
800 header: MessageHeader {
801 num_required_signatures: 1,
802 num_readonly_signed_accounts: 0,
803 num_readonly_unsigned_accounts: 1,
804 },
805 config: v1::TransactionConfig::empty(),
806 lifetime_specifier: Hash::new_unique(),
807 account_keys: vec![Pubkey::new_unique(), Pubkey::new_unique()],
808 instructions: vec![CompiledInstruction {
809 program_id_index: 1,
810 accounts: vec![0],
811 data: vec![],
812 }],
813 }),
814 }
815 }
816
817 fn create_random_ticks(num_ticks: u64, max_hashes_per_tick: u64, mut hash: Hash) -> Vec<Entry> {
818 repeat_with(|| {
819 let hashes_per_tick = rng().random_range(1..max_hashes_per_tick);
820 next_entry_mut(&mut hash, hashes_per_tick, vec![])
821 })
822 .take(num_ticks as usize)
823 .collect()
824 }
825
826 #[test]
827 fn test_entry_verify() {
828 let zero = Hash::default();
829 let one = hash(zero.as_ref());
830 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)); }
835
836 fn test_verify_transactions<Tx: TransactionWithMeta + Send + Sync + 'static>(
837 entries: Vec<EntryView<Bytes>>,
838 skip_verification: bool,
839 thread_pool: &ThreadPool,
840 verify: impl Fn(UnsanitizedTransactionView<Bytes>) -> Result<Tx> + Send + Sync,
841 ) -> bool {
842 let num_txs = entries.iter().map(|entry| entry.transactions.len()).sum();
843 let txs = validate_and_hash_transactions(entries, num_txs, thread_pool, verify);
844 let Ok(txs) = txs else {
845 return false;
846 };
847 skip_verification || txs.unverified_signatures.verify().is_ok()
848 }
849
850 #[test]
851 fn test_entry_transaction_verify() {
852 let zero = Hash::default();
853
854 let keypair = Keypair::new();
856 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
857 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
858 let e0 = Entry::new(&zero, 0, vec![tx0, tx1]);
859 assert!(e0.verify(&zero));
860 let tx2 = system_transaction::transfer(&keypair, &keypair.pubkey(), 2, zero);
861 let tx3 = system_transaction::transfer(&keypair, &keypair.pubkey(), 3, zero);
862 let e1 = Entry::new(&zero, 0, vec![tx2, tx3]);
863 assert!(e1.verify(&zero));
864
865 let es = vec![entry_view_from_entry(e0), entry_view_from_entry(e1)];
866 let thread_pool = ThreadPoolBuilder::new().build().unwrap();
867
868 let verify_transaction = {
870 move |unsanitized: UnsanitizedTransactionView<Bytes>| -> Result<ReplayTransaction> {
871 let sanitized = unsanitized
872 .sanitize(&sanitize_config())
873 .map_err(|_| TransactionError::SanitizeFailure)?;
874 let statically_loaded =
875 RuntimeTransaction::<SanitizedTransactionView<Bytes>>::try_new(
876 sanitized,
877 MessageHash::Compute,
878 None,
879 )?;
880 ReplayTransaction::try_new(
881 statically_loaded,
882 None,
883 &ReservedAccountKeys::empty_key_set(),
884 )
885 }
886 };
887
888 assert!(test_verify_transactions(
889 es,
890 false,
891 &thread_pool,
892 verify_transaction
893 ));
894 }
895
896 #[test]
897 fn test_validate_and_hash_transactions_and_signatures() {
898 let thread_pool = ThreadPoolBuilder::new().build().unwrap();
899 let zero = Hash::default();
900 let keypair = Keypair::new();
901 let tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
902 let entries = vec![entry_view_from_entry(Entry::new(&zero, 0, vec![tx]))];
903
904 let validate_and_hash_transaction =
905 move |unsanitized: UnsanitizedTransactionView<Bytes>| -> Result<ReplayTransaction> {
906 let sanitized = unsanitized
907 .sanitize(&sanitize_config())
908 .map_err(|_| TransactionError::SanitizeFailure)?;
909 let statically_loaded =
910 RuntimeTransaction::<SanitizedTransactionView<Bytes>>::try_new(
911 sanitized,
912 MessageHash::Compute,
913 None,
914 )?;
915 ReplayTransaction::try_new(
916 statically_loaded,
917 None,
918 &ReservedAccountKeys::empty_key_set(),
919 )
920 };
921 let txs =
922 validate_and_hash_transactions(entries, 1, &thread_pool, validate_and_hash_transaction)
923 .expect("transaction validation and hashing must not verify signatures");
924 assert_eq!(txs.entries.len(), 1);
925 assert!(txs.unverified_signatures.verify().is_ok());
926
927 let mut tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
928 tx.signatures[0] = solana_signature::Signature::default();
929 let entries = vec![entry_view_from_entry(Entry::new(&zero, 0, vec![tx]))];
930 let txs =
931 validate_and_hash_transactions(entries, 1, &thread_pool, validate_and_hash_transaction)
932 .expect("transaction validation and hashing must not verify signatures");
933 assert_eq!(txs.entries.len(), 1);
934 assert!(matches!(
935 txs.unverified_signatures.verify(),
936 Err(solana_transaction_error::TransactionError::SignatureFailure)
937 ));
938 }
939
940 #[test]
941 fn test_transaction_reorder_attack() {
942 let zero = Hash::default();
943
944 let keypair = Keypair::new();
946 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
947 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
948 let mut e0 = Entry::new(&zero, 0, vec![tx0.clone(), tx1.clone()]);
949 assert!(e0.verify(&zero));
950
951 e0.transactions[0] = tx1.into(); e0.transactions[1] = tx0.into();
954 assert!(!e0.verify(&zero));
955 }
956
957 #[test]
958 fn test_transaction_signing() {
959 let thread_pool = thread_pool_for_tests();
960
961 use solana_signature::Signature;
962 let zero = Hash::default();
963
964 let keypair = Keypair::new();
965 let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
966 let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
967
968 let mut e0 = [Entry::new(&zero, 0, vec![tx0, tx1])];
970 assert!(e0.verify(&zero, &thread_pool).status());
971
972 let orig_sig = e0[0].transactions[0].signatures[0];
974 e0[0].transactions[0].signatures[0] = Signature::default();
975 assert!(!e0.verify(&zero, &thread_pool).status());
976
977 e0[0].transactions[0].signatures[0] = orig_sig;
979 assert!(e0.verify(&zero, &thread_pool).status());
980
981 let len = e0[0].transactions[0].signatures.len();
983 e0[0].transactions[0]
984 .signatures
985 .resize(len - 1, Signature::default());
986 assert!(!e0.verify(&zero, &thread_pool).status());
987
988 let e0 = [Entry::new(&zero, 0, vec![])];
990 assert!(e0.verify(&zero, &thread_pool).status());
991 }
992
993 #[test]
994 fn test_next_entry() {
995 let zero = Hash::default();
996 let tick = next_entry(&zero, 1, vec![]);
997 assert_eq!(tick.num_hashes, 1);
998 assert_ne!(tick.hash, zero);
999
1000 let tick = next_entry(&zero, 0, vec![]);
1001 assert_eq!(tick.num_hashes, 0);
1002 assert_eq!(tick.hash, zero);
1003
1004 let keypair = Keypair::new();
1005 let tx0 = system_transaction::transfer(&keypair, &Pubkey::new_unique(), 42, zero);
1006 let entry0 = next_entry(&zero, 1, vec![tx0.clone()]);
1007 assert_eq!(entry0.num_hashes, 1);
1008 assert_eq!(entry0.hash, next_hash(&zero, 1, &[tx0.into()]));
1009 }
1010
1011 #[test]
1012 #[should_panic]
1013 fn test_next_entry_panic() {
1014 let zero = Hash::default();
1015 let keypair = Keypair::new();
1016 let tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
1017 next_entry(&zero, 0, vec![tx]);
1018 }
1019
1020 #[test]
1021 fn test_verify_slice1() {
1022 agave_logger::setup();
1023 let thread_pool = thread_pool_for_tests();
1024
1025 let zero = Hash::default();
1026 let one = hash(zero.as_ref());
1027 assert!(vec![][..].verify(&zero, &thread_pool).status());
1029 assert!(
1031 vec![Entry::new_tick(0, &zero)][..]
1032 .verify(&zero, &thread_pool)
1033 .status()
1034 );
1035 assert!(
1037 !vec![Entry::new_tick(0, &zero)][..]
1038 .verify(&one, &thread_pool)
1039 .status()
1040 );
1041 assert!(
1043 vec![next_entry(&zero, 0, vec![]); 2][..]
1044 .verify(&zero, &thread_pool)
1045 .status()
1046 );
1047
1048 let mut bad_ticks = vec![next_entry(&zero, 0, vec![]); 2];
1049 bad_ticks[1].hash = one;
1050 assert!(!bad_ticks.verify(&zero, &thread_pool).status());
1052 }
1053
1054 #[test]
1055 fn test_verify_slice_with_hashes1() {
1056 agave_logger::setup();
1057 let thread_pool = thread_pool_for_tests();
1058
1059 let zero = Hash::default();
1060 let one = hash(zero.as_ref());
1061 let two = hash(one.as_ref());
1062 assert!(vec![][..].verify(&one, &thread_pool).status());
1064 assert!(
1066 vec![Entry::new_tick(1, &two)][..]
1067 .verify(&one, &thread_pool)
1068 .status()
1069 );
1070 assert!(
1072 !vec![Entry::new_tick(1, &two)][..]
1073 .verify(&two, &thread_pool)
1074 .status()
1075 );
1076
1077 let mut ticks = vec![next_entry(&one, 1, vec![])];
1078 ticks.push(next_entry(&ticks.last().unwrap().hash, 1, vec![]));
1079 assert!(ticks.verify(&one, &thread_pool).status());
1081
1082 let mut bad_ticks = vec![next_entry(&one, 1, vec![])];
1083 bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![]));
1084 bad_ticks[1].hash = one;
1085 assert!(!bad_ticks.verify(&one, &thread_pool).status());
1087 }
1088
1089 #[test]
1090 fn test_verify_slice_with_hashes_and_transactions() {
1091 agave_logger::setup();
1092 let thread_pool = thread_pool_for_tests();
1093
1094 let zero = Hash::default();
1095 let one = hash(zero.as_ref());
1096 let two = hash(one.as_ref());
1097 let alice_keypair = Keypair::new();
1098 let bob_keypair = Keypair::new();
1099 let tx0 = system_transaction::transfer(&alice_keypair, &bob_keypair.pubkey(), 1, one);
1100 let tx1 = system_transaction::transfer(&bob_keypair, &alice_keypair.pubkey(), 1, one);
1101 assert!(vec![][..].verify(&one, &thread_pool).status());
1103 assert!(
1105 vec![next_entry(&one, 1, vec![tx0.clone()])][..]
1106 .verify(&one, &thread_pool)
1107 .status()
1108 );
1109 assert!(
1111 !vec![next_entry(&one, 1, vec![tx0.clone()])][..]
1112 .verify(&two, &thread_pool)
1113 .status()
1114 );
1115
1116 let mut ticks = vec![next_entry(&one, 1, vec![tx0.clone()])];
1117 ticks.push(next_entry(
1118 &ticks.last().unwrap().hash,
1119 1,
1120 vec![tx1.clone()],
1121 ));
1122
1123 assert!(ticks.verify(&one, &thread_pool).status());
1125
1126 let mut bad_ticks = vec![next_entry(&one, 1, vec![tx0])];
1127 bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![tx1]));
1128 bad_ticks[1].hash = one;
1129 assert!(!bad_ticks.verify(&one, &thread_pool).status());
1131 }
1132
1133 #[test]
1134 fn test_verify_tick_hash_count() {
1135 let hashes_per_tick = 10;
1136 let tx = VersionedTransaction::default();
1137
1138 let no_hash_tx_entry = Entry {
1139 transactions: vec![tx.clone()],
1140 ..Entry::default()
1141 };
1142 let single_hash_tx_entry = Entry {
1143 transactions: vec![tx.clone()],
1144 num_hashes: 1,
1145 ..Entry::default()
1146 };
1147 let partial_tx_entry = Entry {
1148 num_hashes: hashes_per_tick - 1,
1149 transactions: vec![tx.clone()],
1150 ..Entry::default()
1151 };
1152 let full_tx_entry = Entry {
1153 num_hashes: hashes_per_tick,
1154 transactions: vec![tx.clone()],
1155 ..Entry::default()
1156 };
1157 let max_hash_tx_entry = Entry {
1158 transactions: vec![tx],
1159 num_hashes: u64::MAX,
1160 ..Entry::default()
1161 };
1162
1163 let no_hash_tick_entry = Entry::new_tick(0, &Hash::default());
1164 let single_hash_tick_entry = Entry::new_tick(1, &Hash::default());
1165 let partial_tick_entry = Entry::new_tick(hashes_per_tick - 1, &Hash::default());
1166 let full_tick_entry = Entry::new_tick(hashes_per_tick, &Hash::default());
1167 let max_hash_tick_entry = Entry::new_tick(u64::MAX, &Hash::default());
1168
1169 let mut tick_hash_count = 0;
1171 let mut entries: Vec<Entry> = vec![];
1172 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1173 assert_eq!(tick_hash_count, 0);
1174
1175 tick_hash_count = hashes_per_tick;
1177 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1178 assert_eq!(tick_hash_count, hashes_per_tick);
1179 tick_hash_count = 0;
1180
1181 entries = vec![max_hash_tx_entry.clone()];
1183 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, 0));
1184 assert_eq!(tick_hash_count, 0);
1185
1186 entries = vec![partial_tick_entry.clone()];
1188 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1189 assert_eq!(tick_hash_count, hashes_per_tick - 1);
1190 tick_hash_count = 0;
1191
1192 entries = vec![no_hash_tx_entry, full_tick_entry.clone()];
1194 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1195 assert_eq!(tick_hash_count, 0);
1196
1197 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick - 1));
1199 assert_eq!(tick_hash_count, hashes_per_tick);
1200 tick_hash_count = 0;
1201
1202 entries = vec![partial_tx_entry];
1204 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1205 assert_eq!(tick_hash_count, hashes_per_tick - 1);
1206 tick_hash_count = 0;
1207
1208 entries = vec![no_hash_tick_entry.clone()];
1210 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1211 assert_eq!(tick_hash_count, 0);
1212
1213 entries = vec![full_tx_entry.clone(), no_hash_tick_entry];
1215 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1216 assert_eq!(tick_hash_count, hashes_per_tick);
1217 tick_hash_count = 0;
1218
1219 entries = vec![full_tx_entry.clone(), single_hash_tick_entry.clone()];
1221 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1222 assert_eq!(tick_hash_count, hashes_per_tick + 1);
1223 tick_hash_count = 0;
1224
1225 entries = vec![full_tx_entry];
1227 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1228 assert_eq!(tick_hash_count, hashes_per_tick);
1229 tick_hash_count = 0;
1230
1231 entries = vec![single_hash_tx_entry.clone(), partial_tick_entry];
1233 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1234 assert_eq!(tick_hash_count, 0);
1235
1236 let tx_entries: Vec<Entry> = (0..hashes_per_tick - 1)
1238 .map(|_| single_hash_tx_entry.clone())
1239 .collect();
1240 entries = [tx_entries, vec![single_hash_tick_entry]].concat();
1241 assert!(entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1242 assert_eq!(tick_hash_count, 0);
1243
1244 entries = vec![full_tick_entry.clone(), max_hash_tick_entry];
1246 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1247 assert_eq!(tick_hash_count, u64::MAX);
1248 tick_hash_count = 0;
1249
1250 entries = vec![max_hash_tx_entry, full_tick_entry];
1252 assert!(!entries.verify_tick_hash_count(&mut tick_hash_count, hashes_per_tick));
1253 assert_eq!(tick_hash_count, u64::MAX);
1254 }
1255
1256 #[test]
1257 fn test_poh_verify_fuzz() {
1258 agave_logger::setup();
1259 for _ in 0..100 {
1260 let mut time = Measure::start("ticks");
1261 let num_ticks = rng().random_range(1..100);
1262 info!("create {num_ticks} ticks:");
1263 let mut entries = create_random_ticks(num_ticks, 100, Hash::default());
1264 time.stop();
1265
1266 let mut modified = false;
1267 if rng().random_ratio(1, 2) {
1268 modified = true;
1269 let modify_idx = rng().random_range(0..num_ticks) as usize;
1270 entries[modify_idx].hash = hash(&[1, 2, 3]);
1271 }
1272
1273 info!("done.. {time}");
1274 let mut time = Measure::start("poh");
1275 let res = entries
1276 .verify(&Hash::default(), &thread_pool_for_tests())
1277 .status();
1278 assert_eq!(res, !modified);
1279 time.stop();
1280 info!("{time} {res}");
1281 }
1282 }
1283
1284 #[test]
1285 fn test_hash_transactions() {
1286 let mut transactions: Vec<_> = [test_tx(), test_tx(), test_tx()]
1287 .into_iter()
1288 .map(VersionedTransaction::from)
1289 .collect();
1290
1291 let hash1 = hash_transactions(&transactions);
1294 transactions.swap(0, 1);
1295 let hash2 = hash_transactions(&transactions);
1296 assert_ne!(hash1, hash2);
1297 }
1298
1299 #[test]
1300 fn test_deserialize_entries_rejects_txv1_unknown_config_mask_bit() {
1301 let tx = simple_v1_transaction_for_deserialization_tests();
1302 let entries = vec![next_versioned_entry(&Hash::default(), 1, vec![tx.clone()])];
1303 let mut serialized_entries = wincode::serialize(&entries).unwrap();
1304
1305 assert!(wincode::deserialize::<Vec<Entry>>(&serialized_entries).is_ok());
1306
1307 let serialized_tx = wincode::serialize(&tx).unwrap();
1308 let tx_offset = serialized_entries
1309 .windows(serialized_tx.len())
1310 .position(|window| window == serialized_tx)
1311 .expect("serialized transaction should be embedded in serialized entry");
1312
1313 const TXV1_CONFIG_MASK_OFFSET: usize = 1 + 3;
1315 let mask_offset = tx_offset + TXV1_CONFIG_MASK_OFFSET;
1316 let mask_range = mask_offset..mask_offset + core::mem::size_of::<u32>();
1317 let mask = u32::from_le_bytes(serialized_entries[mask_range.clone()].try_into().unwrap());
1318 assert_eq!(mask, 0);
1319
1320 let unknown_config_mask_bit = 1u32
1321 .checked_shl(v1::TransactionConfigMask::KNOWN_BITS.trailing_ones())
1322 .expect("txv1 config mask should have at least one unknown bit");
1323 assert_ne!(
1324 unknown_config_mask_bit & v1::TransactionConfigMask::KNOWN_BITS,
1325 unknown_config_mask_bit
1326 );
1327 serialized_entries[mask_range]
1328 .copy_from_slice(&(mask | unknown_config_mask_bit).to_le_bytes());
1329
1330 assert!(matches!(
1331 wincode::deserialize::<Vec<Entry>>(&serialized_entries),
1332 Err(wincode::ReadError::InvalidValue(
1333 "invalid transaction config mask"
1334 ))
1335 ));
1336 }
1337}