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(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/// 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 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    /// Creates the next Entry `num_hashes` after `start_hash`.
238    pub fn new(prev_hash: &Hash, mut num_hashes: u64, transactions: Vec<Transaction>) -> Self {
239        // If you passed in transactions, but passed in num_hashes == 0, then
240        // next_hash will generate the next hash and set num_hashes == 1
241        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    /// Verifies self.hash is the result of hashing a `start_hash` `self.num_hashes` times.
276    /// If the transaction is not a Tick, then hash that as well.
277    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    // a hash of a slice of transactions only needs to hash the signatures
297    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
323/// Creates the hash `num_hashes` after `start_hash`. If the transaction contains a signature, the
324/// final hash will be a hash of both the previous ID and the signature.  If num_hashes is zero and
325/// there's no transaction data, start_hash is returned.
326pub 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
393/// Validates and hashes the transactions included in the given entries.
394///
395/// The function does NOT verify transaction signatures. The caller is expected to call
396/// `UnverifiedSignatures::verify()` on the returned `unverified_signatures`.
397pub 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
495// an EntrySlice is a slice of Entries
496pub trait EntrySlice {
497    /// Verifies the hashes and counts of a slice of transactions are all consistent.
498    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    /// Checks that each entry tick has the correct number of hashes. Entry slices do not
502    /// necessarily end in a tick, so `tick_hash_count` is used to carry over the hash count
503    /// for the next entry slice.
504    fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool;
505    /// Counts tick entries
506    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        // When hashes_per_tick is 0, hashing is disabled.
527        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
567/// Creates the next Tick or Transaction Entry `num_hashes` after `start_hash`.
568pub 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
573/// Creates the next Tick or Transaction Entry `num_hashes` after `start_hash`.
574pub 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    // Allocate fewer threads for unit tests
589    // Unit tests typically aren't creating massive blocks to verify, and
590    // multiple tests could be running in parallel so any further parallelism
591    // will do more harm than good
592    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)); // base case, never used
671        assert!(!Entry::new_tick(0, &zero).verify(&one)); // base case, bad
672        assert!(next_entry(&zero, 1, vec![]).verify(&zero)); // inductive step
673        assert!(!next_entry(&zero, 1, vec![]).verify(&one)); // inductive step, bad
674    }
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        // First, verify entries
695        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        // Next, verify entry slice
709        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        // First, verify entries
779        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        // Next, swap two transactions and ensure verification fails.
786        e0.transactions[0] = tx1.into(); // <-- attack
787        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        // Verify entry with 2 transactions
803        let mut e0 = [Entry::new(&zero, 0, vec![tx0, tx1])];
804        assert!(e0.verify(&zero, &thread_pool).status());
805
806        // Clear signature of the first transaction, see that it does not verify
807        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        // restore original signature
812        e0[0].transactions[0].signatures[0] = orig_sig;
813        assert!(e0.verify(&zero, &thread_pool).status());
814
815        // Resize signatures and see verification fails.
816        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        // Pass an entry with no transactions
823        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        // base case
862        assert!(vec![][..].verify(&zero, &thread_pool).status());
863        // singleton case 1
864        assert!(
865            vec![Entry::new_tick(0, &zero)][..]
866                .verify(&zero, &thread_pool)
867                .status()
868        );
869        // singleton case 2, bad
870        assert!(
871            !vec![Entry::new_tick(0, &zero)][..]
872                .verify(&one, &thread_pool)
873                .status()
874        );
875        // inductive step
876        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        // inductive step, bad
885        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        // base case
897        assert!(vec![][..].verify(&one, &thread_pool).status());
898        // singleton case 1
899        assert!(
900            vec![Entry::new_tick(1, &two)][..]
901                .verify(&one, &thread_pool)
902                .status()
903        );
904        // singleton case 2, bad
905        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        // inductive step
914        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        // inductive step, bad
920        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        // base case
936        assert!(vec![][..].verify(&one, &thread_pool).status());
937        // singleton case 1
938        assert!(
939            vec![next_entry(&one, 1, vec![tx0.clone()])][..]
940                .verify(&one, &thread_pool)
941                .status()
942        );
943        // singleton case 2, bad
944        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        // inductive step
958        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        // inductive step, bad
964        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        // empty batch should succeed if hashes_per_tick hasn't been reached
1004        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        // empty batch should fail if hashes_per_tick has been reached
1010        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        // validation is disabled when hashes_per_tick == 0
1016        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        // partial tick should fail
1021        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        // full tick entry should succeed
1027        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        // oversized tick entry should fail
1032        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        // partial tx entry without tick entry should succeed
1037        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        // no hash tick entry should fail
1043        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        // full tx entry with no hash tick entry should still fail
1048        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        // full tx entry with oversized tick entry should fail
1054        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        // full tx entry without tick entry should fail
1060        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        // tx entry and a tick should succeed
1066        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        // many tx entries and a tick should succeed
1071        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        // check overflow saturation should fail
1079        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        // check overflow saturation should fail
1085        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        // Test different permutations of the transactions have different final hashes.
1126        // i.e. that **order** of transactions is included in the hash.
1127        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        // txv1 begins with the version byte and the 3-byte legacy header.
1148        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}