Skip to main content

solana_entry/
entry.rs

1//! The `entry` module is a fundamental building block of Proof of History. It contains a
2//! unique ID that is the hash of the Entry before it, plus the hash of the
3//! transactions within it. Entries cannot be reordered, and its field `num_hashes`
4//! represents an approximate amount of time since the last Entry was created.
5use {
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/// Each Entry contains three pieces of data. The `num_hashes` field is the number
31/// of hashes performed since the previous entry.  The `hash` field is the result
32/// of hashing `hash` from the previous entry `num_hashes` times.  The `transactions`
33/// field points to Transactions that took place shortly before `hash` was generated.
34///
35/// If you multiply `num_hashes` by the amount of time it takes to generate a new hash, you
36/// get a duration estimate since the last `Entry`. Since processing power increases
37/// over time, one should expect the duration `num_hashes` represents to decrease proportionally.
38/// An upper bound on Duration can be estimated by assuming each hash was generated by the
39/// world's fastest processor at the time the entry was recorded. Or said another way, it
40/// is physically not possible for a shorter duration to have occurred if one assumes the
41/// hash was computed by the world's fastest processor at that time. The hash chain is both
42/// a Verifiable Delay Function (VDF) and a Proof of Work (not to be confused with Proof of
43/// Work consensus!)
44#[derive(Debug, Default, PartialEq, Eq, Clone, SchemaWrite, SchemaRead)]
45pub struct Entry {
46    /// The number of hashes since the previous Entry ID.
47    pub num_hashes: u64,
48
49    /// The SHA-256 hash `num_hashes` after the previous Entry ID.
50    pub hash: Hash,
51
52    /// An ordered list of transactions that were observed before the Entry ID was
53    /// generated. They may have been observed before a previous Entry ID but were
54    /// pushed back into this list to ensure deterministic interpretation of the ledger.
55    #[wincode(with = "WincodeVec<VersionedTransaction, MaxDataShredsLen>")]
56    pub transactions: Vec<VersionedTransaction>,
57}
58
59// The data needed to verify an Entry.
60#[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
122/// Typed entry to distinguish between transaction and tick entries
123pub 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/// TODO: we will move this API into solana-sdk.
138#[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    /// todo: this function is for benches only and will be removed after we move the batch verify logic to sdk
176    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    /// Creates the next Entry `num_hashes` after `start_hash`.
214    pub fn new(prev_hash: &Hash, mut num_hashes: u64, transactions: Vec<Transaction>) -> Self {
215        // If you passed in transactions, but passed in num_hashes == 0, then
216        // next_hash will generate the next hash and set num_hashes == 1
217        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    /// Verifies self.hash is the result of hashing a `start_hash` `self.num_hashes` times.
252    /// If the transaction is not a Tick, then hash that as well.
253    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    // a hash of a slice of transactions only needs to hash the signatures
273    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
299/// Creates the hash `num_hashes` after `start_hash`. If the transaction contains a signature, the
300/// final hash will be a hash of both the previous ID and the signature.  If num_hashes is zero and
301/// there's no transaction data, start_hash is returned.
302pub 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
369/// Validates and hashes the transactions included in the given entries.
370///
371/// The function does NOT verify transaction signatures. The caller is expected to call
372/// `UnverifiedSignatures::verify()` on the returned `unverified_signatures`.
373pub 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
471// an EntrySlice is a slice of Entries
472pub trait EntrySlice {
473    /// Verifies the hashes and counts of a slice of transactions are all consistent.
474    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    /// Checks that each entry tick has the correct number of hashes. Entry slices do not
478    /// necessarily end in a tick, so `tick_hash_count` is used to carry over the hash count
479    /// for the next entry slice.
480    fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool;
481    /// Counts tick entries
482    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        // When hashes_per_tick is 0, hashing is disabled.
503        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
543/// Creates the next Tick or Transaction Entry `num_hashes` after `start_hash`.
544pub 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
549/// Creates the next Tick or Transaction Entry `num_hashes` after `start_hash`.
550pub 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    // Allocate fewer threads for unit tests
565    // Unit tests typically aren't creating massive blocks to verify, and
566    // multiple tests could be running in parallel so any further parallelism
567    // will do more harm than good
568    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)); // base case, never used
647        assert!(!Entry::new_tick(0, &zero).verify(&one)); // base case, bad
648        assert!(next_entry(&zero, 1, vec![]).verify(&zero)); // inductive step
649        assert!(!next_entry(&zero, 1, vec![]).verify(&one)); // inductive step, bad
650    }
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        // First, verify entries
671        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        // Next, verify entry slice
685        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        // First, verify entries
757        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        // Next, swap two transactions and ensure verification fails.
764        e0.transactions[0] = tx1.into(); // <-- attack
765        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        // Verify entry with 2 transactions
781        let mut e0 = [Entry::new(&zero, 0, vec![tx0, tx1])];
782        assert!(e0.verify(&zero, &thread_pool).status());
783
784        // Clear signature of the first transaction, see that it does not verify
785        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        // restore original signature
790        e0[0].transactions[0].signatures[0] = orig_sig;
791        assert!(e0.verify(&zero, &thread_pool).status());
792
793        // Resize signatures and see verification fails.
794        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        // Pass an entry with no transactions
801        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        // base case
840        assert!(vec![][..].verify(&zero, &thread_pool).status());
841        // singleton case 1
842        assert!(
843            vec![Entry::new_tick(0, &zero)][..]
844                .verify(&zero, &thread_pool)
845                .status()
846        );
847        // singleton case 2, bad
848        assert!(
849            !vec![Entry::new_tick(0, &zero)][..]
850                .verify(&one, &thread_pool)
851                .status()
852        );
853        // inductive step
854        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        // inductive step, bad
863        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        // base case
875        assert!(vec![][..].verify(&one, &thread_pool).status());
876        // singleton case 1
877        assert!(
878            vec![Entry::new_tick(1, &two)][..]
879                .verify(&one, &thread_pool)
880                .status()
881        );
882        // singleton case 2, bad
883        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        // inductive step
892        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        // inductive step, bad
898        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        // base case
914        assert!(vec![][..].verify(&one, &thread_pool).status());
915        // singleton case 1
916        assert!(
917            vec![next_entry(&one, 1, vec![tx0.clone()])][..]
918                .verify(&one, &thread_pool)
919                .status()
920        );
921        // singleton case 2, bad
922        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        // inductive step
936        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        // inductive step, bad
942        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        // empty batch should succeed if hashes_per_tick hasn't been reached
982        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        // empty batch should fail if hashes_per_tick has been reached
988        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        // validation is disabled when hashes_per_tick == 0
994        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        // partial tick should fail
999        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        // full tick entry should succeed
1005        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        // oversized tick entry should fail
1010        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        // partial tx entry without tick entry should succeed
1015        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        // no hash tick entry should fail
1021        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        // full tx entry with no hash tick entry should still fail
1026        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        // full tx entry with oversized tick entry should fail
1032        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        // full tx entry without tick entry should fail
1038        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        // tx entry and a tick should succeed
1044        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        // many tx entries and a tick should succeed
1049        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        // check overflow saturation should fail
1057        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        // check overflow saturation should fail
1063        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        // Test different permutations of the transactions have different final hashes.
1104        // i.e. that **order** of transactions is included in the hash.
1105        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        // txv1 begins with the version byte and the 3-byte legacy header.
1126        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}