|
@@ -11,28 +11,16 @@ use {
|
|
|
rayon::{prelude::*, ThreadPool},
|
|
rayon::{prelude::*, ThreadPool},
|
|
|
serde::{Deserialize, Serialize},
|
|
serde::{Deserialize, Serialize},
|
|
|
solana_hash::Hash,
|
|
solana_hash::Hash,
|
|
|
- solana_measure::measure::Measure,
|
|
|
|
|
solana_merkle_tree::MerkleTree,
|
|
solana_merkle_tree::MerkleTree,
|
|
|
- solana_metrics::*,
|
|
|
|
|
- solana_packet::Meta,
|
|
|
|
|
- solana_perf::{
|
|
|
|
|
- cuda_runtime::PinnedVec,
|
|
|
|
|
- packet::{Packet, PacketBatch, PacketBatchRecycler, PinnedPacketBatch, PACKETS_PER_BATCH},
|
|
|
|
|
- perf_libs,
|
|
|
|
|
- recycler::Recycler,
|
|
|
|
|
- sigverify,
|
|
|
|
|
- },
|
|
|
|
|
solana_runtime_transaction::transaction_with_meta::TransactionWithMeta,
|
|
solana_runtime_transaction::transaction_with_meta::TransactionWithMeta,
|
|
|
solana_transaction::{
|
|
solana_transaction::{
|
|
|
versioned::VersionedTransaction, Transaction, TransactionVerificationMode,
|
|
versioned::VersionedTransaction, Transaction, TransactionVerificationMode,
|
|
|
},
|
|
},
|
|
|
- solana_transaction_error::{TransactionError, TransactionResult as Result},
|
|
|
|
|
|
|
+ solana_transaction_error::TransactionResult as Result,
|
|
|
std::{
|
|
std::{
|
|
|
- cmp,
|
|
|
|
|
ffi::OsStr,
|
|
ffi::OsStr,
|
|
|
iter::repeat_with,
|
|
iter::repeat_with,
|
|
|
- sync::{Arc, Mutex, Once, OnceLock},
|
|
|
|
|
- thread::{self, JoinHandle},
|
|
|
|
|
|
|
+ sync::{Arc, Once, OnceLock},
|
|
|
time::Instant,
|
|
time::Instant,
|
|
|
},
|
|
},
|
|
|
wincode::{containers::Pod, SchemaRead, SchemaWrite},
|
|
wincode::{containers::Pod, SchemaRead, SchemaWrite},
|
|
@@ -250,146 +238,33 @@ pub fn next_hash(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/// Last action required to verify an entry
|
|
|
|
|
-enum VerifyAction {
|
|
|
|
|
- /// Mixin a hash before computing the last hash for a transaction entry
|
|
|
|
|
- Mixin(Hash),
|
|
|
|
|
- /// Compute one last hash for a tick entry
|
|
|
|
|
- Tick,
|
|
|
|
|
- /// No action needed (tick entry with no hashes)
|
|
|
|
|
- None,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-pub struct GpuVerificationData {
|
|
|
|
|
- thread_h: Option<JoinHandle<u64>>,
|
|
|
|
|
- hashes: Option<Arc<Mutex<PinnedVec<Hash>>>>,
|
|
|
|
|
- verifications: Option<Vec<(VerifyAction, Hash)>>,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-pub enum DeviceVerificationData {
|
|
|
|
|
- Cpu(),
|
|
|
|
|
- Gpu(GpuVerificationData),
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
pub struct EntryVerificationState {
|
|
pub struct EntryVerificationState {
|
|
|
- verification_status: EntryVerificationStatus,
|
|
|
|
|
|
|
+ verification_status: bool,
|
|
|
poh_duration_us: u64,
|
|
poh_duration_us: u64,
|
|
|
- device_verification_data: DeviceVerificationData,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-pub struct GpuSigVerificationData {
|
|
|
|
|
- thread_h: Option<JoinHandle<(bool, u64)>>,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-pub enum DeviceSigVerificationData {
|
|
|
|
|
- Cpu(),
|
|
|
|
|
- Gpu(GpuSigVerificationData),
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub struct EntrySigVerificationState<Tx: TransactionWithMeta> {
|
|
pub struct EntrySigVerificationState<Tx: TransactionWithMeta> {
|
|
|
- verification_status: EntryVerificationStatus,
|
|
|
|
|
|
|
+ verification_status: bool,
|
|
|
entries: Option<Vec<EntryType<Tx>>>,
|
|
entries: Option<Vec<EntryType<Tx>>>,
|
|
|
- device_verification_data: DeviceSigVerificationData,
|
|
|
|
|
- gpu_verify_duration_us: u64,
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl<Tx: TransactionWithMeta> EntrySigVerificationState<Tx> {
|
|
impl<Tx: TransactionWithMeta> EntrySigVerificationState<Tx> {
|
|
|
pub fn entries(&mut self) -> Option<Vec<EntryType<Tx>>> {
|
|
pub fn entries(&mut self) -> Option<Vec<EntryType<Tx>>> {
|
|
|
self.entries.take()
|
|
self.entries.take()
|
|
|
}
|
|
}
|
|
|
- pub fn finish_verify(&mut self) -> bool {
|
|
|
|
|
- match &mut self.device_verification_data {
|
|
|
|
|
- DeviceSigVerificationData::Gpu(verification_state) => {
|
|
|
|
|
- let (verified, gpu_time_us) =
|
|
|
|
|
- verification_state.thread_h.take().unwrap().join().unwrap();
|
|
|
|
|
- self.gpu_verify_duration_us = gpu_time_us;
|
|
|
|
|
- self.verification_status = if verified {
|
|
|
|
|
- EntryVerificationStatus::Success
|
|
|
|
|
- } else {
|
|
|
|
|
- EntryVerificationStatus::Failure
|
|
|
|
|
- };
|
|
|
|
|
- verified
|
|
|
|
|
- }
|
|
|
|
|
- DeviceSigVerificationData::Cpu() => {
|
|
|
|
|
- self.verification_status == EntryVerificationStatus::Success
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- pub fn status(&self) -> EntryVerificationStatus {
|
|
|
|
|
|
|
+ pub fn status(&self) -> bool {
|
|
|
self.verification_status
|
|
self.verification_status
|
|
|
}
|
|
}
|
|
|
- pub fn gpu_verify_duration(&self) -> u64 {
|
|
|
|
|
- self.gpu_verify_duration_us
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-#[derive(Default, Clone)]
|
|
|
|
|
-pub struct VerifyRecyclers {
|
|
|
|
|
- hash_recycler: Recycler<PinnedVec<Hash>>,
|
|
|
|
|
- tick_count_recycler: Recycler<PinnedVec<u64>>,
|
|
|
|
|
- packet_recycler: PacketBatchRecycler,
|
|
|
|
|
- out_recycler: Recycler<PinnedVec<u8>>,
|
|
|
|
|
- tx_offset_recycler: Recycler<sigverify::TxOffset>,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
|
|
|
|
-pub enum EntryVerificationStatus {
|
|
|
|
|
- Failure,
|
|
|
|
|
- Success,
|
|
|
|
|
- Pending,
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl EntryVerificationState {
|
|
impl EntryVerificationState {
|
|
|
- pub fn status(&self) -> EntryVerificationStatus {
|
|
|
|
|
|
|
+ pub fn status(&self) -> bool {
|
|
|
self.verification_status
|
|
self.verification_status
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub fn poh_duration_us(&self) -> u64 {
|
|
pub fn poh_duration_us(&self) -> u64 {
|
|
|
self.poh_duration_us
|
|
self.poh_duration_us
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- pub fn finish_verify(&mut self, thread_pool: &ThreadPool) -> bool {
|
|
|
|
|
- match &mut self.device_verification_data {
|
|
|
|
|
- DeviceVerificationData::Gpu(verification_state) => {
|
|
|
|
|
- let gpu_time_us = verification_state.thread_h.take().unwrap().join().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- let mut verify_check_time = Measure::start("verify_check");
|
|
|
|
|
- let hashes = verification_state.hashes.take().unwrap();
|
|
|
|
|
- let hashes = Arc::try_unwrap(hashes)
|
|
|
|
|
- .expect("unwrap Arc")
|
|
|
|
|
- .into_inner()
|
|
|
|
|
- .expect("into_inner");
|
|
|
|
|
- let res = thread_pool.install(|| {
|
|
|
|
|
- hashes
|
|
|
|
|
- .into_par_iter()
|
|
|
|
|
- .cloned()
|
|
|
|
|
- .zip(verification_state.verifications.take().unwrap())
|
|
|
|
|
- .all(|(hash, (action, expected))| {
|
|
|
|
|
- let actual = match action {
|
|
|
|
|
- VerifyAction::Mixin(mixin) => {
|
|
|
|
|
- Poh::new(hash, None).record(mixin).unwrap().hash
|
|
|
|
|
- }
|
|
|
|
|
- VerifyAction::Tick => Poh::new(hash, None).tick().unwrap().hash,
|
|
|
|
|
- VerifyAction::None => hash,
|
|
|
|
|
- };
|
|
|
|
|
- actual == expected
|
|
|
|
|
- })
|
|
|
|
|
- });
|
|
|
|
|
- verify_check_time.stop();
|
|
|
|
|
- self.poh_duration_us += gpu_time_us + verify_check_time.as_us();
|
|
|
|
|
-
|
|
|
|
|
- self.verification_status = if res {
|
|
|
|
|
- EntryVerificationStatus::Success
|
|
|
|
|
- } else {
|
|
|
|
|
- EntryVerificationStatus::Failure
|
|
|
|
|
- };
|
|
|
|
|
- res
|
|
|
|
|
- }
|
|
|
|
|
- DeviceVerificationData::Cpu() => {
|
|
|
|
|
- self.verification_status == EntryVerificationStatus::Success
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub fn verify_transactions<Tx: TransactionWithMeta + Send + Sync>(
|
|
pub fn verify_transactions<Tx: TransactionWithMeta + Send + Sync>(
|
|
@@ -421,37 +296,11 @@ pub fn start_verify_transactions<Tx: TransactionWithMeta + Send + Sync + 'static
|
|
|
entries: Vec<Entry>,
|
|
entries: Vec<Entry>,
|
|
|
skip_verification: bool,
|
|
skip_verification: bool,
|
|
|
thread_pool: &ThreadPool,
|
|
thread_pool: &ThreadPool,
|
|
|
- verify_recyclers: VerifyRecyclers,
|
|
|
|
|
verify: Arc<
|
|
verify: Arc<
|
|
|
dyn Fn(VersionedTransaction, TransactionVerificationMode) -> Result<Tx> + Send + Sync,
|
|
dyn Fn(VersionedTransaction, TransactionVerificationMode) -> Result<Tx> + Send + Sync,
|
|
|
>,
|
|
>,
|
|
|
) -> Result<EntrySigVerificationState<Tx>> {
|
|
) -> Result<EntrySigVerificationState<Tx>> {
|
|
|
- let api = perf_libs::api();
|
|
|
|
|
-
|
|
|
|
|
- // Use the CPU if we have too few transactions for GPU signature verification to be worth it.
|
|
|
|
|
- // We will also use the CPU if no acceleration API is used or if we're skipping
|
|
|
|
|
- // the signature verification as we'd have nothing to do on the GPU in that case.
|
|
|
|
|
- // TODO: make the CPU-to GPU crossover point dynamic, perhaps based on similar future
|
|
|
|
|
- // heuristics to what might be used in sigverify::ed25519_verify when a dynamic crossover
|
|
|
|
|
- // is introduced for that function (see TODO in sigverify::ed25519_verify)
|
|
|
|
|
- let use_cpu = skip_verification
|
|
|
|
|
- || api.is_none()
|
|
|
|
|
- || entries
|
|
|
|
|
- .iter()
|
|
|
|
|
- .try_fold(0, |accum: usize, entry: &Entry| -> Option<usize> {
|
|
|
|
|
- if accum.saturating_add(entry.transactions.len()) < 512 {
|
|
|
|
|
- Some(accum.saturating_add(entry.transactions.len()))
|
|
|
|
|
- } else {
|
|
|
|
|
- None
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- .is_some();
|
|
|
|
|
-
|
|
|
|
|
- if use_cpu {
|
|
|
|
|
- start_verify_transactions_cpu(entries, skip_verification, thread_pool, verify)
|
|
|
|
|
- } else {
|
|
|
|
|
- start_verify_transactions_gpu(entries, verify_recyclers, thread_pool, verify)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ start_verify_transactions_cpu(entries, skip_verification, thread_pool, verify)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fn start_verify_transactions_cpu<Tx: TransactionWithMeta + Send + Sync + 'static>(
|
|
fn start_verify_transactions_cpu<Tx: TransactionWithMeta + Send + Sync + 'static>(
|
|
@@ -475,115 +324,8 @@ fn start_verify_transactions_cpu<Tx: TransactionWithMeta + Send + Sync + 'static
|
|
|
let entries = verify_transactions(entries, thread_pool, Arc::new(verify_func))?;
|
|
let entries = verify_transactions(entries, thread_pool, Arc::new(verify_func))?;
|
|
|
|
|
|
|
|
Ok(EntrySigVerificationState {
|
|
Ok(EntrySigVerificationState {
|
|
|
- verification_status: EntryVerificationStatus::Success,
|
|
|
|
|
- entries: Some(entries),
|
|
|
|
|
- device_verification_data: DeviceSigVerificationData::Cpu(),
|
|
|
|
|
- gpu_verify_duration_us: 0,
|
|
|
|
|
- })
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-fn start_verify_transactions_gpu<Tx: TransactionWithMeta + Send + Sync + 'static>(
|
|
|
|
|
- entries: Vec<Entry>,
|
|
|
|
|
- verify_recyclers: VerifyRecyclers,
|
|
|
|
|
- thread_pool: &ThreadPool,
|
|
|
|
|
- verify: Arc<
|
|
|
|
|
- dyn Fn(VersionedTransaction, TransactionVerificationMode) -> Result<Tx> + Send + Sync,
|
|
|
|
|
- >,
|
|
|
|
|
-) -> Result<EntrySigVerificationState<Tx>> {
|
|
|
|
|
- let verify_func = {
|
|
|
|
|
- move |versioned_tx: VersionedTransaction| -> Result<Tx> {
|
|
|
|
|
- verify(versioned_tx, TransactionVerificationMode::HashOnly)
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let entries = verify_transactions(entries, thread_pool, Arc::new(verify_func))?;
|
|
|
|
|
-
|
|
|
|
|
- let transactions = entries
|
|
|
|
|
- .iter()
|
|
|
|
|
- .filter_map(|entry_type| match entry_type {
|
|
|
|
|
- EntryType::Tick(_) => None,
|
|
|
|
|
- EntryType::Transactions(transactions) => Some(transactions),
|
|
|
|
|
- })
|
|
|
|
|
- .flatten()
|
|
|
|
|
- .collect::<Vec<_>>();
|
|
|
|
|
-
|
|
|
|
|
- if transactions.is_empty() {
|
|
|
|
|
- return Ok(EntrySigVerificationState {
|
|
|
|
|
- verification_status: EntryVerificationStatus::Success,
|
|
|
|
|
- entries: Some(entries),
|
|
|
|
|
- device_verification_data: DeviceSigVerificationData::Cpu(),
|
|
|
|
|
- gpu_verify_duration_us: 0,
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let packet_batches = thread_pool.install(|| {
|
|
|
|
|
- transactions
|
|
|
|
|
- .par_chunks(PACKETS_PER_BATCH)
|
|
|
|
|
- .map(|transaction_chunk| {
|
|
|
|
|
- let num_transactions = transaction_chunk.len();
|
|
|
|
|
- let mut packet_batch = PinnedPacketBatch::new_with_recycler(
|
|
|
|
|
- &verify_recyclers.packet_recycler,
|
|
|
|
|
- num_transactions,
|
|
|
|
|
- "entry-sig-verify",
|
|
|
|
|
- );
|
|
|
|
|
- // We use set_len here instead of resize(num_txs, Packet::default()), to save
|
|
|
|
|
- // memory bandwidth and avoid writing a large amount of data that will be overwritten
|
|
|
|
|
- // soon afterwards. As well, Packet::default() actually leaves the packet data
|
|
|
|
|
- // uninitialized, so the initialization would simply write junk into
|
|
|
|
|
- // the vector anyway.
|
|
|
|
|
- unsafe {
|
|
|
|
|
- packet_batch.set_len(num_transactions);
|
|
|
|
|
- }
|
|
|
|
|
- let transaction_iter = transaction_chunk
|
|
|
|
|
- .iter()
|
|
|
|
|
- .map(|tx| tx.to_versioned_transaction());
|
|
|
|
|
-
|
|
|
|
|
- let res = packet_batch
|
|
|
|
|
- .iter_mut()
|
|
|
|
|
- .zip(transaction_iter)
|
|
|
|
|
- .all(|(packet, tx)| {
|
|
|
|
|
- *packet.meta_mut() = Meta::default();
|
|
|
|
|
- Packet::populate_packet(packet, None, &tx).is_ok()
|
|
|
|
|
- });
|
|
|
|
|
- if res {
|
|
|
|
|
- Ok(PacketBatch::from(packet_batch))
|
|
|
|
|
- } else {
|
|
|
|
|
- Err(TransactionError::SanitizeFailure)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- .collect::<Result<Vec<_>>>()
|
|
|
|
|
- });
|
|
|
|
|
- let mut packet_batches = packet_batches?;
|
|
|
|
|
-
|
|
|
|
|
- let tx_offset_recycler = verify_recyclers.tx_offset_recycler;
|
|
|
|
|
- let out_recycler = verify_recyclers.out_recycler;
|
|
|
|
|
- let num_packets = transactions.len();
|
|
|
|
|
- let gpu_verify_thread = thread::Builder::new()
|
|
|
|
|
- .name("solGpuSigVerify".into())
|
|
|
|
|
- .spawn(move || {
|
|
|
|
|
- let mut verify_time = Measure::start("sigverify");
|
|
|
|
|
- sigverify::ed25519_verify(
|
|
|
|
|
- &mut packet_batches,
|
|
|
|
|
- &tx_offset_recycler,
|
|
|
|
|
- &out_recycler,
|
|
|
|
|
- false,
|
|
|
|
|
- num_packets,
|
|
|
|
|
- );
|
|
|
|
|
- let verified = packet_batches
|
|
|
|
|
- .iter()
|
|
|
|
|
- .all(|batch| batch.iter().all(|p| !p.meta().discard()));
|
|
|
|
|
- verify_time.stop();
|
|
|
|
|
- (verified, verify_time.as_us())
|
|
|
|
|
- })
|
|
|
|
|
- .unwrap();
|
|
|
|
|
-
|
|
|
|
|
- Ok(EntrySigVerificationState {
|
|
|
|
|
- verification_status: EntryVerificationStatus::Pending,
|
|
|
|
|
|
|
+ verification_status: true,
|
|
|
entries: Some(entries),
|
|
entries: Some(entries),
|
|
|
- device_verification_data: DeviceSigVerificationData::Gpu(GpuSigVerificationData {
|
|
|
|
|
- thread_h: Some(gpu_verify_thread),
|
|
|
|
|
- }),
|
|
|
|
|
- gpu_verify_duration_us: 0,
|
|
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -616,13 +358,7 @@ pub trait EntrySlice {
|
|
|
simd_len: usize,
|
|
simd_len: usize,
|
|
|
thread_pool: &ThreadPool,
|
|
thread_pool: &ThreadPool,
|
|
|
) -> EntryVerificationState;
|
|
) -> EntryVerificationState;
|
|
|
- fn start_verify(
|
|
|
|
|
- &self,
|
|
|
|
|
- start_hash: &Hash,
|
|
|
|
|
- thread_pool: &ThreadPool,
|
|
|
|
|
- recyclers: VerifyRecyclers,
|
|
|
|
|
- ) -> EntryVerificationState;
|
|
|
|
|
- fn verify(&self, start_hash: &Hash, thread_pool: &ThreadPool) -> bool;
|
|
|
|
|
|
|
+ fn verify(&self, start_hash: &Hash, thread_pool: &ThreadPool) -> EntryVerificationState;
|
|
|
/// Checks that each entry tick has the correct number of hashes. Entry slices do not
|
|
/// Checks that each entry tick has the correct number of hashes. Entry slices do not
|
|
|
/// necessarily end in a tick, so `tick_hash_count` is used to carry over the hash count
|
|
/// necessarily end in a tick, so `tick_hash_count` is used to carry over the hash count
|
|
|
/// for the next entry slice.
|
|
/// for the next entry slice.
|
|
@@ -632,9 +368,8 @@ pub trait EntrySlice {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl EntrySlice for [Entry] {
|
|
impl EntrySlice for [Entry] {
|
|
|
- fn verify(&self, start_hash: &Hash, thread_pool: &ThreadPool) -> bool {
|
|
|
|
|
- self.start_verify(start_hash, thread_pool, VerifyRecyclers::default())
|
|
|
|
|
- .finish_verify(thread_pool)
|
|
|
|
|
|
|
+ fn verify(&self, start_hash: &Hash, thread_pool: &ThreadPool) -> EntryVerificationState {
|
|
|
|
|
+ self.verify_cpu(start_hash, thread_pool)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fn verify_cpu_generic(
|
|
fn verify_cpu_generic(
|
|
@@ -665,13 +400,8 @@ impl EntrySlice for [Entry] {
|
|
|
});
|
|
});
|
|
|
let poh_duration_us = now.elapsed().as_micros() as u64;
|
|
let poh_duration_us = now.elapsed().as_micros() as u64;
|
|
|
EntryVerificationState {
|
|
EntryVerificationState {
|
|
|
- verification_status: if res {
|
|
|
|
|
- EntryVerificationStatus::Success
|
|
|
|
|
- } else {
|
|
|
|
|
- EntryVerificationStatus::Failure
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ verification_status: res,
|
|
|
poh_duration_us,
|
|
poh_duration_us,
|
|
|
- device_verification_data: DeviceVerificationData::Cpu(),
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -753,13 +483,8 @@ impl EntrySlice for [Entry] {
|
|
|
});
|
|
});
|
|
|
let poh_duration_us = now.elapsed().as_micros() as u64;
|
|
let poh_duration_us = now.elapsed().as_micros() as u64;
|
|
|
EntryVerificationState {
|
|
EntryVerificationState {
|
|
|
- verification_status: if res {
|
|
|
|
|
- EntryVerificationStatus::Success
|
|
|
|
|
- } else {
|
|
|
|
|
- EntryVerificationStatus::Failure
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ verification_status: res,
|
|
|
poh_duration_us,
|
|
poh_duration_us,
|
|
|
- device_verification_data: DeviceVerificationData::Cpu(),
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -785,100 +510,6 @@ impl EntrySlice for [Entry] {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- fn start_verify(
|
|
|
|
|
- &self,
|
|
|
|
|
- start_hash: &Hash,
|
|
|
|
|
- thread_pool: &ThreadPool,
|
|
|
|
|
- recyclers: VerifyRecyclers,
|
|
|
|
|
- ) -> EntryVerificationState {
|
|
|
|
|
- let start = Instant::now();
|
|
|
|
|
- let Some(api) = perf_libs::api() else {
|
|
|
|
|
- return self.verify_cpu(start_hash, thread_pool);
|
|
|
|
|
- };
|
|
|
|
|
- inc_new_counter_info!("entry_verify-num_entries", self.len());
|
|
|
|
|
-
|
|
|
|
|
- let genesis = [Entry {
|
|
|
|
|
- num_hashes: 0,
|
|
|
|
|
- hash: *start_hash,
|
|
|
|
|
- transactions: vec![],
|
|
|
|
|
- }];
|
|
|
|
|
-
|
|
|
|
|
- let hashes: Vec<Hash> = genesis
|
|
|
|
|
- .iter()
|
|
|
|
|
- .chain(self)
|
|
|
|
|
- .map(|entry| entry.hash)
|
|
|
|
|
- .take(self.len())
|
|
|
|
|
- .collect();
|
|
|
|
|
-
|
|
|
|
|
- let mut hashes_pinned = recyclers.hash_recycler.allocate("poh_verify_hash");
|
|
|
|
|
- hashes_pinned.set_pinnable();
|
|
|
|
|
- hashes_pinned.resize(hashes.len(), Hash::default());
|
|
|
|
|
- hashes_pinned.copy_from_slice(&hashes);
|
|
|
|
|
-
|
|
|
|
|
- let mut num_hashes_vec = recyclers
|
|
|
|
|
- .tick_count_recycler
|
|
|
|
|
- .allocate("poh_verify_num_hashes");
|
|
|
|
|
- num_hashes_vec.reserve_and_pin(cmp::max(1, self.len()));
|
|
|
|
|
- for entry in self {
|
|
|
|
|
- num_hashes_vec.push(entry.num_hashes.saturating_sub(1));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let length = self.len();
|
|
|
|
|
- let hashes = Arc::new(Mutex::new(hashes_pinned));
|
|
|
|
|
- let hashes_clone = hashes.clone();
|
|
|
|
|
-
|
|
|
|
|
- let gpu_verify_thread = thread::Builder::new()
|
|
|
|
|
- .name("solGpuPohVerify".into())
|
|
|
|
|
- .spawn(move || {
|
|
|
|
|
- let mut hashes = hashes_clone.lock().unwrap();
|
|
|
|
|
- let gpu_wait = Instant::now();
|
|
|
|
|
- let res;
|
|
|
|
|
- unsafe {
|
|
|
|
|
- res = (api.poh_verify_many)(
|
|
|
|
|
- hashes.as_mut_ptr() as *mut u8,
|
|
|
|
|
- num_hashes_vec.as_ptr(),
|
|
|
|
|
- length,
|
|
|
|
|
- 1,
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- assert!(res == 0, "GPU PoH verify many failed");
|
|
|
|
|
- inc_new_counter_info!(
|
|
|
|
|
- "entry_verify-gpu_thread",
|
|
|
|
|
- gpu_wait.elapsed().as_micros() as usize
|
|
|
|
|
- );
|
|
|
|
|
- gpu_wait.elapsed().as_micros() as u64
|
|
|
|
|
- })
|
|
|
|
|
- .unwrap();
|
|
|
|
|
-
|
|
|
|
|
- let verifications = thread_pool.install(|| {
|
|
|
|
|
- self.into_par_iter()
|
|
|
|
|
- .map(|entry| {
|
|
|
|
|
- let answer = entry.hash;
|
|
|
|
|
- let action = if entry.transactions.is_empty() {
|
|
|
|
|
- if entry.num_hashes == 0 {
|
|
|
|
|
- VerifyAction::None
|
|
|
|
|
- } else {
|
|
|
|
|
- VerifyAction::Tick
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- VerifyAction::Mixin(hash_transactions(&entry.transactions))
|
|
|
|
|
- };
|
|
|
|
|
- (action, answer)
|
|
|
|
|
- })
|
|
|
|
|
- .collect()
|
|
|
|
|
- });
|
|
|
|
|
- let device_verification_data = DeviceVerificationData::Gpu(GpuVerificationData {
|
|
|
|
|
- thread_h: Some(gpu_verify_thread),
|
|
|
|
|
- verifications: Some(verifications),
|
|
|
|
|
- hashes: Some(hashes),
|
|
|
|
|
- });
|
|
|
|
|
- EntryVerificationState {
|
|
|
|
|
- verification_status: EntryVerificationStatus::Pending,
|
|
|
|
|
- poh_duration_us: start.elapsed().as_micros() as u64,
|
|
|
|
|
- device_verification_data,
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool {
|
|
fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool {
|
|
|
// When hashes_per_tick is 0, hashing is disabled.
|
|
// When hashes_per_tick is 0, hashing is disabled.
|
|
|
if hashes_per_tick == 0 {
|
|
if hashes_per_tick == 0 {
|
|
@@ -973,10 +604,12 @@ mod tests {
|
|
|
use {
|
|
use {
|
|
|
super::*,
|
|
super::*,
|
|
|
agave_reserved_account_keys::ReservedAccountKeys,
|
|
agave_reserved_account_keys::ReservedAccountKeys,
|
|
|
|
|
+ rayon::ThreadPoolBuilder,
|
|
|
solana_hash::Hash,
|
|
solana_hash::Hash,
|
|
|
solana_keypair::Keypair,
|
|
solana_keypair::Keypair,
|
|
|
|
|
+ solana_measure::measure::Measure,
|
|
|
solana_message::SimpleAddressLoader,
|
|
solana_message::SimpleAddressLoader,
|
|
|
- solana_perf::test_tx::{test_invalid_tx, test_tx},
|
|
|
|
|
|
|
+ solana_perf::test_tx::test_tx,
|
|
|
solana_pubkey::Pubkey,
|
|
solana_pubkey::Pubkey,
|
|
|
solana_runtime_transaction::runtime_transaction::RuntimeTransaction,
|
|
solana_runtime_transaction::runtime_transaction::RuntimeTransaction,
|
|
|
solana_sha256_hasher::hash,
|
|
solana_sha256_hasher::hash,
|
|
@@ -1002,7 +635,6 @@ mod tests {
|
|
|
fn test_verify_transactions<Tx: TransactionWithMeta + Send + Sync + 'static>(
|
|
fn test_verify_transactions<Tx: TransactionWithMeta + Send + Sync + 'static>(
|
|
|
entries: Vec<Entry>,
|
|
entries: Vec<Entry>,
|
|
|
skip_verification: bool,
|
|
skip_verification: bool,
|
|
|
- verify_recyclers: VerifyRecyclers,
|
|
|
|
|
thread_pool: &ThreadPool,
|
|
thread_pool: &ThreadPool,
|
|
|
verify: Arc<
|
|
verify: Arc<
|
|
|
dyn Fn(VersionedTransaction, TransactionVerificationMode) -> Result<Tx> + Send + Sync,
|
|
dyn Fn(VersionedTransaction, TransactionVerificationMode) -> Result<Tx> + Send + Sync,
|
|
@@ -1022,57 +654,35 @@ mod tests {
|
|
|
|
|
|
|
|
let cpu_verify_result =
|
|
let cpu_verify_result =
|
|
|
verify_transactions(entries.clone(), thread_pool, Arc::new(verify_func));
|
|
verify_transactions(entries.clone(), thread_pool, Arc::new(verify_func));
|
|
|
- let mut gpu_verify_result: EntrySigVerificationState<Tx> = {
|
|
|
|
|
- let verify_result = start_verify_transactions(
|
|
|
|
|
- entries,
|
|
|
|
|
- skip_verification,
|
|
|
|
|
- thread_pool,
|
|
|
|
|
- verify_recyclers,
|
|
|
|
|
- verify,
|
|
|
|
|
- );
|
|
|
|
|
- match verify_result {
|
|
|
|
|
- Ok(res) => res,
|
|
|
|
|
- _ => EntrySigVerificationState {
|
|
|
|
|
- verification_status: EntryVerificationStatus::Failure,
|
|
|
|
|
- entries: None,
|
|
|
|
|
- device_verification_data: DeviceSigVerificationData::Cpu(),
|
|
|
|
|
- gpu_verify_duration_us: 0,
|
|
|
|
|
- },
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
|
|
|
|
|
- match cpu_verify_result {
|
|
|
|
|
- Ok(_) => {
|
|
|
|
|
- assert!(gpu_verify_result.verification_status != EntryVerificationStatus::Failure);
|
|
|
|
|
- assert!(gpu_verify_result.finish_verify());
|
|
|
|
|
- true
|
|
|
|
|
- }
|
|
|
|
|
- _ => {
|
|
|
|
|
- assert!(
|
|
|
|
|
- gpu_verify_result.verification_status == EntryVerificationStatus::Failure
|
|
|
|
|
- || !gpu_verify_result.finish_verify()
|
|
|
|
|
- );
|
|
|
|
|
- false
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ cpu_verify_result.is_ok()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
#[test]
|
|
|
- fn test_entry_gpu_verify() {
|
|
|
|
|
- let thread_pool = thread_pool_for_tests();
|
|
|
|
|
|
|
+ fn test_entry_transaction_verify() {
|
|
|
|
|
+ let zero = Hash::default();
|
|
|
|
|
+
|
|
|
|
|
+ // First, verify entries
|
|
|
|
|
+ let keypair = Keypair::new();
|
|
|
|
|
+ let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
|
|
|
|
|
+ let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
|
|
|
|
|
+ let e0 = Entry::new(&zero, 0, vec![tx0, tx1]);
|
|
|
|
|
+ assert!(e0.verify(&zero));
|
|
|
|
|
+ let tx2 = system_transaction::transfer(&keypair, &keypair.pubkey(), 2, zero);
|
|
|
|
|
+ let tx3 = system_transaction::transfer(&keypair, &keypair.pubkey(), 3, zero);
|
|
|
|
|
+ let e1 = Entry::new(&zero, 0, vec![tx2, tx3]);
|
|
|
|
|
+ assert!(e1.verify(&zero));
|
|
|
|
|
|
|
|
|
|
+ let es = vec![e0, e1];
|
|
|
|
|
+ let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
|
|
|
|
+
|
|
|
|
|
+ // Next, verify entry slice
|
|
|
let verify_transaction = {
|
|
let verify_transaction = {
|
|
|
move |versioned_tx: VersionedTransaction,
|
|
move |versioned_tx: VersionedTransaction,
|
|
|
- verification_mode: TransactionVerificationMode|
|
|
|
|
|
|
|
+ _mode: TransactionVerificationMode|
|
|
|
-> Result<RuntimeTransaction<SanitizedTransaction>> {
|
|
-> Result<RuntimeTransaction<SanitizedTransaction>> {
|
|
|
let sanitized_tx = {
|
|
let sanitized_tx = {
|
|
|
- let message_hash =
|
|
|
|
|
- if verification_mode == TransactionVerificationMode::FullVerification {
|
|
|
|
|
- versioned_tx.verify_and_hash_message()?
|
|
|
|
|
- } else {
|
|
|
|
|
- versioned_tx.message.hash()
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
|
|
+ let message_hash = versioned_tx.verify_and_hash_message()?;
|
|
|
RuntimeTransaction::try_create(
|
|
RuntimeTransaction::try_create(
|
|
|
versioned_tx,
|
|
versioned_tx,
|
|
|
MessageHash::Precomputed(message_hash),
|
|
MessageHash::Precomputed(message_hash),
|
|
@@ -1083,38 +693,15 @@ mod tests {
|
|
|
)
|
|
)
|
|
|
}?;
|
|
}?;
|
|
|
|
|
|
|
|
|
|
+ sanitized_tx.verify()?;
|
|
|
|
|
+
|
|
|
Ok(sanitized_tx)
|
|
Ok(sanitized_tx)
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- let recycler = VerifyRecyclers::default();
|
|
|
|
|
-
|
|
|
|
|
- // Make sure we test with a number of transactions that's not a multiple of PACKETS_PER_BATCH
|
|
|
|
|
- let entries_invalid = (0..1025)
|
|
|
|
|
- .map(|_| {
|
|
|
|
|
- let transaction = test_invalid_tx();
|
|
|
|
|
- next_entry_mut(&mut Hash::default(), 0, vec![transaction])
|
|
|
|
|
- })
|
|
|
|
|
- .collect::<Vec<_>>();
|
|
|
|
|
-
|
|
|
|
|
- let entries_valid = (0..1025)
|
|
|
|
|
- .map(|_| {
|
|
|
|
|
- let transaction = test_tx();
|
|
|
|
|
- next_entry_mut(&mut Hash::default(), 0, vec![transaction])
|
|
|
|
|
- })
|
|
|
|
|
- .collect::<Vec<_>>();
|
|
|
|
|
-
|
|
|
|
|
- assert!(!test_verify_transactions(
|
|
|
|
|
- entries_invalid,
|
|
|
|
|
- false,
|
|
|
|
|
- recycler.clone(),
|
|
|
|
|
- &thread_pool,
|
|
|
|
|
- Arc::new(verify_transaction)
|
|
|
|
|
- ));
|
|
|
|
|
assert!(test_verify_transactions(
|
|
assert!(test_verify_transactions(
|
|
|
- entries_valid,
|
|
|
|
|
|
|
+ es,
|
|
|
false,
|
|
false,
|
|
|
- recycler,
|
|
|
|
|
&thread_pool,
|
|
&thread_pool,
|
|
|
Arc::new(verify_transaction)
|
|
Arc::new(verify_transaction)
|
|
|
));
|
|
));
|
|
@@ -1150,27 +737,27 @@ mod tests {
|
|
|
|
|
|
|
|
// Verify entry with 2 transactions
|
|
// Verify entry with 2 transactions
|
|
|
let mut e0 = [Entry::new(&zero, 0, vec![tx0, tx1])];
|
|
let mut e0 = [Entry::new(&zero, 0, vec![tx0, tx1])];
|
|
|
- assert!(e0.verify(&zero, &thread_pool));
|
|
|
|
|
|
|
+ assert!(e0.verify(&zero, &thread_pool).status());
|
|
|
|
|
|
|
|
// Clear signature of the first transaction, see that it does not verify
|
|
// Clear signature of the first transaction, see that it does not verify
|
|
|
let orig_sig = e0[0].transactions[0].signatures[0];
|
|
let orig_sig = e0[0].transactions[0].signatures[0];
|
|
|
e0[0].transactions[0].signatures[0] = Signature::default();
|
|
e0[0].transactions[0].signatures[0] = Signature::default();
|
|
|
- assert!(!e0.verify(&zero, &thread_pool));
|
|
|
|
|
|
|
+ assert!(!e0.verify(&zero, &thread_pool).status());
|
|
|
|
|
|
|
|
// restore original signature
|
|
// restore original signature
|
|
|
e0[0].transactions[0].signatures[0] = orig_sig;
|
|
e0[0].transactions[0].signatures[0] = orig_sig;
|
|
|
- assert!(e0.verify(&zero, &thread_pool));
|
|
|
|
|
|
|
+ assert!(e0.verify(&zero, &thread_pool).status());
|
|
|
|
|
|
|
|
// Resize signatures and see verification fails.
|
|
// Resize signatures and see verification fails.
|
|
|
let len = e0[0].transactions[0].signatures.len();
|
|
let len = e0[0].transactions[0].signatures.len();
|
|
|
e0[0].transactions[0]
|
|
e0[0].transactions[0]
|
|
|
.signatures
|
|
.signatures
|
|
|
.resize(len - 1, Signature::default());
|
|
.resize(len - 1, Signature::default());
|
|
|
- assert!(!e0.verify(&zero, &thread_pool));
|
|
|
|
|
|
|
+ assert!(!e0.verify(&zero, &thread_pool).status());
|
|
|
|
|
|
|
|
// Pass an entry with no transactions
|
|
// Pass an entry with no transactions
|
|
|
let e0 = [Entry::new(&zero, 0, vec![])];
|
|
let e0 = [Entry::new(&zero, 0, vec![])];
|
|
|
- assert!(e0.verify(&zero, &thread_pool));
|
|
|
|
|
|
|
+ assert!(e0.verify(&zero, &thread_pool).status());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
#[test]
|
|
@@ -1208,18 +795,24 @@ mod tests {
|
|
|
let zero = Hash::default();
|
|
let zero = Hash::default();
|
|
|
let one = hash(zero.as_ref());
|
|
let one = hash(zero.as_ref());
|
|
|
// base case
|
|
// base case
|
|
|
- assert!(vec![][..].verify(&zero, &thread_pool));
|
|
|
|
|
|
|
+ assert!(vec![][..].verify(&zero, &thread_pool).status());
|
|
|
// singleton case 1
|
|
// singleton case 1
|
|
|
- assert!(vec![Entry::new_tick(0, &zero)][..].verify(&zero, &thread_pool));
|
|
|
|
|
|
|
+ assert!(vec![Entry::new_tick(0, &zero)][..]
|
|
|
|
|
+ .verify(&zero, &thread_pool)
|
|
|
|
|
+ .status());
|
|
|
// singleton case 2, bad
|
|
// singleton case 2, bad
|
|
|
- assert!(!vec![Entry::new_tick(0, &zero)][..].verify(&one, &thread_pool));
|
|
|
|
|
|
|
+ assert!(!vec![Entry::new_tick(0, &zero)][..]
|
|
|
|
|
+ .verify(&one, &thread_pool)
|
|
|
|
|
+ .status());
|
|
|
// inductive step
|
|
// inductive step
|
|
|
- assert!(vec![next_entry(&zero, 0, vec![]); 2][..].verify(&zero, &thread_pool));
|
|
|
|
|
|
|
+ assert!(vec![next_entry(&zero, 0, vec![]); 2][..]
|
|
|
|
|
+ .verify(&zero, &thread_pool)
|
|
|
|
|
+ .status());
|
|
|
|
|
|
|
|
let mut bad_ticks = vec![next_entry(&zero, 0, vec![]); 2];
|
|
let mut bad_ticks = vec![next_entry(&zero, 0, vec![]); 2];
|
|
|
bad_ticks[1].hash = one;
|
|
bad_ticks[1].hash = one;
|
|
|
// inductive step, bad
|
|
// inductive step, bad
|
|
|
- assert!(!bad_ticks.verify(&zero, &thread_pool));
|
|
|
|
|
|
|
+ assert!(!bad_ticks.verify(&zero, &thread_pool).status());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
#[test]
|
|
@@ -1231,22 +824,26 @@ mod tests {
|
|
|
let one = hash(zero.as_ref());
|
|
let one = hash(zero.as_ref());
|
|
|
let two = hash(one.as_ref());
|
|
let two = hash(one.as_ref());
|
|
|
// base case
|
|
// base case
|
|
|
- assert!(vec![][..].verify(&one, &thread_pool));
|
|
|
|
|
|
|
+ assert!(vec![][..].verify(&one, &thread_pool).status());
|
|
|
// singleton case 1
|
|
// singleton case 1
|
|
|
- assert!(vec![Entry::new_tick(1, &two)][..].verify(&one, &thread_pool));
|
|
|
|
|
|
|
+ assert!(vec![Entry::new_tick(1, &two)][..]
|
|
|
|
|
+ .verify(&one, &thread_pool)
|
|
|
|
|
+ .status());
|
|
|
// singleton case 2, bad
|
|
// singleton case 2, bad
|
|
|
- assert!(!vec![Entry::new_tick(1, &two)][..].verify(&two, &thread_pool));
|
|
|
|
|
|
|
+ assert!(!vec![Entry::new_tick(1, &two)][..]
|
|
|
|
|
+ .verify(&two, &thread_pool)
|
|
|
|
|
+ .status());
|
|
|
|
|
|
|
|
let mut ticks = vec![next_entry(&one, 1, vec![])];
|
|
let mut ticks = vec![next_entry(&one, 1, vec![])];
|
|
|
ticks.push(next_entry(&ticks.last().unwrap().hash, 1, vec![]));
|
|
ticks.push(next_entry(&ticks.last().unwrap().hash, 1, vec![]));
|
|
|
// inductive step
|
|
// inductive step
|
|
|
- assert!(ticks.verify(&one, &thread_pool));
|
|
|
|
|
|
|
+ assert!(ticks.verify(&one, &thread_pool).status());
|
|
|
|
|
|
|
|
let mut bad_ticks = vec![next_entry(&one, 1, vec![])];
|
|
let mut bad_ticks = vec![next_entry(&one, 1, vec![])];
|
|
|
bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![]));
|
|
bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![]));
|
|
|
bad_ticks[1].hash = one;
|
|
bad_ticks[1].hash = one;
|
|
|
// inductive step, bad
|
|
// inductive step, bad
|
|
|
- assert!(!bad_ticks.verify(&one, &thread_pool));
|
|
|
|
|
|
|
+ assert!(!bad_ticks.verify(&one, &thread_pool).status());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
#[test]
|
|
@@ -1262,11 +859,15 @@ mod tests {
|
|
|
let tx0 = system_transaction::transfer(&alice_keypair, &bob_keypair.pubkey(), 1, one);
|
|
let tx0 = system_transaction::transfer(&alice_keypair, &bob_keypair.pubkey(), 1, one);
|
|
|
let tx1 = system_transaction::transfer(&bob_keypair, &alice_keypair.pubkey(), 1, one);
|
|
let tx1 = system_transaction::transfer(&bob_keypair, &alice_keypair.pubkey(), 1, one);
|
|
|
// base case
|
|
// base case
|
|
|
- assert!(vec![][..].verify(&one, &thread_pool));
|
|
|
|
|
|
|
+ assert!(vec![][..].verify(&one, &thread_pool).status());
|
|
|
// singleton case 1
|
|
// singleton case 1
|
|
|
- assert!(vec![next_entry(&one, 1, vec![tx0.clone()])][..].verify(&one, &thread_pool));
|
|
|
|
|
|
|
+ assert!(vec![next_entry(&one, 1, vec![tx0.clone()])][..]
|
|
|
|
|
+ .verify(&one, &thread_pool)
|
|
|
|
|
+ .status());
|
|
|
// singleton case 2, bad
|
|
// singleton case 2, bad
|
|
|
- assert!(!vec![next_entry(&one, 1, vec![tx0.clone()])][..].verify(&two, &thread_pool));
|
|
|
|
|
|
|
+ assert!(!vec![next_entry(&one, 1, vec![tx0.clone()])][..]
|
|
|
|
|
+ .verify(&two, &thread_pool)
|
|
|
|
|
+ .status());
|
|
|
|
|
|
|
|
let mut ticks = vec![next_entry(&one, 1, vec![tx0.clone()])];
|
|
let mut ticks = vec![next_entry(&one, 1, vec![tx0.clone()])];
|
|
|
ticks.push(next_entry(
|
|
ticks.push(next_entry(
|
|
@@ -1276,13 +877,13 @@ mod tests {
|
|
|
));
|
|
));
|
|
|
|
|
|
|
|
// inductive step
|
|
// inductive step
|
|
|
- assert!(ticks.verify(&one, &thread_pool));
|
|
|
|
|
|
|
+ assert!(ticks.verify(&one, &thread_pool).status());
|
|
|
|
|
|
|
|
let mut bad_ticks = vec![next_entry(&one, 1, vec![tx0])];
|
|
let mut bad_ticks = vec![next_entry(&one, 1, vec![tx0])];
|
|
|
bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![tx1]));
|
|
bad_ticks.push(next_entry(&bad_ticks.last().unwrap().hash, 1, vec![tx1]));
|
|
|
bad_ticks[1].hash = one;
|
|
bad_ticks[1].hash = one;
|
|
|
// inductive step, bad
|
|
// inductive step, bad
|
|
|
- assert!(!bad_ticks.verify(&one, &thread_pool));
|
|
|
|
|
|
|
+ assert!(!bad_ticks.verify(&one, &thread_pool).status());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
#[test]
|
|
@@ -1421,7 +1022,9 @@ mod tests {
|
|
|
|
|
|
|
|
info!("done.. {time}");
|
|
info!("done.. {time}");
|
|
|
let mut time = Measure::start("poh");
|
|
let mut time = Measure::start("poh");
|
|
|
- let res = entries.verify(&Hash::default(), &thread_pool_for_tests());
|
|
|
|
|
|
|
+ let res = entries
|
|
|
|
|
+ .verify(&Hash::default(), &thread_pool_for_tests())
|
|
|
|
|
+ .status();
|
|
|
assert_eq!(res, !modified);
|
|
assert_eq!(res, !modified);
|
|
|
time.stop();
|
|
time.stop();
|
|
|
info!("{time} {res}");
|
|
info!("{time} {res}");
|