do not verify headers twice

This commit is contained in:
Svyatoslav Nikolsky 2019-04-03 15:42:13 +03:00
parent f69e0a00a1
commit 2626366c7c
13 changed files with 173 additions and 94 deletions

1
Cargo.lock generated
View File

@ -1876,6 +1876,7 @@ version = "0.1.0"
dependencies = [
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitcrypto 0.1.0",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"chain 0.1.0",
"db 0.1.0",

View File

@ -104,7 +104,7 @@ pub fn main(benchmark: &mut Benchmark) {
// bench
benchmark.start();
for block in verification_blocks.iter() {
chain_verifier.verify(VerificationLevel::Full, block).unwrap();
chain_verifier.verify(VerificationLevel::FULL, block).unwrap();
}
benchmark.stop();
}

View File

@ -126,15 +126,15 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
let services = Services::default().with_network(true);
let verification_level = match matches.value_of("verification-level") {
Some(s) if s == "full" => VerificationLevel::Full,
Some(s) if s == "header" => VerificationLevel::Header,
Some(s) if s == "none" => VerificationLevel::NoVerification,
Some(s) if s == "full" => VerificationLevel::FULL,
Some(s) if s == "header" => VerificationLevel::HEADER,
Some(s) if s == "none" => VerificationLevel::NO_VERIFICATION,
Some(s) => return Err(format!("Invalid verification level: {}", s)),
None => VerificationLevel::Full,
None => VerificationLevel::FULL,
};
let verification_edge = match matches.value_of("verification-edge") {
Some(s) if verification_level != VerificationLevel::Full => {
Some(s) if verification_level != VerificationLevel::FULL => {
let edge: H256 = s.parse().map_err(|_| "Invalid verification edge".to_owned())?;
edge.reversed()
},

View File

@ -6,8 +6,11 @@ use storage;
use network::ConsensusParams;
use primitives::hash::H256;
use super::Error;
use synchronization_verifier::{Verifier, SyncVerifier, VerificationTask, HeadersVerificationSink,
VerificationSink, BlockVerificationSink, TransactionVerificationSink};
use synchronization_verifier::{
Verifier, SyncVerifier, VerificationTask, HeadersVerificationSink,
VerificationSink, BlockVerificationSink, TransactionVerificationSink,
PartiallyVerifiedBlock,
};
use types::{PeerIndex, StorageRef};
use utils::OrphanBlocksPool;
use VerificationParameters;
@ -76,7 +79,7 @@ impl BlocksWriter {
let mut verification_queue: VecDeque<chain::IndexedBlock> = self.orphaned_blocks_pool.remove_blocks_for_parent(block.hash());
verification_queue.push_front(block);
while let Some(block) = verification_queue.pop_front() {
self.verifier.verify_block(block);
self.verifier.verify_block(PartiallyVerifiedBlock::NotVerified(block));
if let Some(err) = self.sink.error() {
return Err(err);
}
@ -165,7 +168,7 @@ mod tests {
fn default_verification_params() -> VerificationParameters {
VerificationParameters {
verification_level: VerificationLevel::Full,
verification_level: VerificationLevel::FULL,
verification_edge: 0u8.into(),
}
}

View File

@ -305,18 +305,15 @@ impl Chain {
scheduled
}
/// Add block to verifying queue
pub fn verify_block(&mut self, header: IndexedBlockHeader) {
/// Add block to verifying queue.
///
/// Returns true if the header has already been in the headers chain. The fact that it is in the
/// chain, guarantees the header has already been pre-verified. The opposite isn't true -
/// if the header isn't in the chain, it could have been (in rare cases) pre-verified.
pub fn verify_block(&mut self, header: IndexedBlockHeader) -> bool {
// insert header to the in-memory chain in case when it is not already there (non-headers-first sync)
self.hash_chain.push_back_at(VERIFYING_QUEUE, header.hash.clone());
self.headers_chain.insert(header);
}
/// Add blocks to verifying queue
pub fn verify_blocks(&mut self, blocks: Vec<IndexedBlockHeader>) {
for block in blocks {
self.verify_block(block);
}
self.headers_chain.insert(header)
}
/// Moves n blocks from requested queue to verifying queue

View File

@ -14,8 +14,10 @@ use synchronization_chain::{Chain, BlockState, TransactionState, BlockInsertionR
use synchronization_executor::{Task, TaskExecutor};
use synchronization_manager::ManagementWorker;
use synchronization_peers_tasks::PeersTasks;
use synchronization_verifier::{VerificationSink, HeadersVerificationSink, BlockVerificationSink,
TransactionVerificationSink, VerificationTask};
use synchronization_verifier::{
VerificationSink, HeadersVerificationSink, BlockVerificationSink,
TransactionVerificationSink, VerificationTask, PartiallyVerifiedBlock,
};
use types::{BlockHeight, ClientCoreRef, PeersRef, PeerIndex, SynchronizationStateRef, EmptyBoxFuture, SyncListenerRef};
use utils::{AverageSpeedMeter, OrphanBlocksPool, OrphanTransactionsPool, HashPosition};
#[cfg(test)] use synchronization_peers_tasks::{Information as PeersTasksInformation};
@ -68,7 +70,7 @@ pub trait ClientCore {
fn on_disconnect(&mut self, peer_index: PeerIndex);
fn on_inventory(&self, peer_index: PeerIndex, message: types::Inv);
fn on_headers(&mut self, peer_index: PeerIndex, message: types::Headers) -> Option<Vec<IndexedBlockHeader>>;
fn on_block(&mut self, peer_index: PeerIndex, block: IndexedBlock) -> Option<VecDeque<IndexedBlock>>;
fn on_block(&mut self, peer_index: PeerIndex, block: IndexedBlock) -> Option<VecDeque<PartiallyVerifiedBlock>>;
fn on_transaction(&mut self, peer_index: PeerIndex, transaction: IndexedTransaction) -> Option<VecDeque<IndexedTransaction>>;
fn on_notfound(&mut self, peer_index: PeerIndex, message: types::NotFound);
fn after_peer_nearly_blocks_verified(&mut self, peer_index: PeerIndex, future: EmptyBoxFuture);
@ -354,12 +356,12 @@ impl<T> ClientCore for SynchronizationClientCore<T> where T: TaskExecutor {
Some(headers)
}
fn on_block(&mut self, peer_index: PeerIndex, block: IndexedBlock) -> Option<VecDeque<IndexedBlock>> {
fn on_block(&mut self, peer_index: PeerIndex, block: IndexedBlock) -> Option<VecDeque<PartiallyVerifiedBlock>> {
// update peers to select next tasks
self.peers_tasks.on_block_received(peer_index, &block.header.hash);
// prepare list of blocks to verify + make all required changes to the chain
let mut result: Option<VecDeque<IndexedBlock>> = None;
let mut result = None;
let block_state = self.chain.block_state(&block.header.hash);
match block_state {
BlockState::Verifying | BlockState::Stored => {
@ -419,27 +421,29 @@ impl<T> ClientCore for SynchronizationClientCore<T> where T: TaskExecutor {
self.sync_speed_meter.checkpoint();
// remember peer as useful
self.peers_tasks.useful_peer(peer_index);
// schedule verification
let mut blocks_to_verify: VecDeque<IndexedBlock> = VecDeque::new();
blocks_to_verify.extend(self.orphaned_blocks_pool.remove_blocks_for_parent(&block.header.hash));
blocks_to_verify.push_front(block);
// forget blocks we are going to process
let blocks_hashes_to_forget: Vec<_> = blocks_to_verify.iter().map(|b| b.hash().clone()).collect();
self.chain.forget_blocks_leave_header(&blocks_hashes_to_forget);
let mut blocks_to_verify_hashes = vec![*block.hash()];
let orphaned_blocks = self.orphaned_blocks_pool.remove_blocks_for_parent(&block.header.hash);
blocks_to_verify_hashes.extend(orphaned_blocks.iter().map(IndexedBlock::hash).cloned());
self.chain.forget_blocks_leave_header(&blocks_to_verify_hashes);
// remember that we are verifying these blocks
let blocks_headers: Vec<_> = blocks_to_verify.iter().map(|b| b.header.clone()).collect();
self.chain.verify_blocks(blocks_headers);
let blocks_to_verify = ::std::iter::once(block).chain(orphaned_blocks)
.map(|block| if self.chain.verify_block(block.header.clone()) {
PartiallyVerifiedBlock::HeaderPreVerified(block)
} else {
PartiallyVerifiedBlock::NotVerified(block)
})
.collect::<VecDeque<_>>();
// remember that we are verifying block from this peer
for verifying_block_hash in blocks_to_verify.iter().map(|b| b.hash().clone()) {
self.verifying_blocks_by_peer.insert(verifying_block_hash, peer_index);
for verifying_block_hash in &blocks_to_verify_hashes {
self.verifying_blocks_by_peer.insert(*verifying_block_hash, peer_index);
}
match self.verifying_blocks_futures.entry(peer_index) {
Entry::Occupied(mut entry) => {
entry.get_mut().0.extend(blocks_to_verify.iter().map(|b| b.hash().clone()));
entry.get_mut().0.extend(blocks_to_verify_hashes);
},
Entry::Vacant(entry) => {
let block_hashes: HashSet<_> = blocks_to_verify.iter().map(|b| b.hash().clone()).collect();
entry.insert((block_hashes, Vec::new()));
entry.insert((blocks_to_verify_hashes.into_iter().collect(), Vec::new()));
}
}
result = Some(blocks_to_verify);

View File

@ -14,6 +14,15 @@ use types::{PeerIndex, BlockHeight, StorageRef, MemoryPoolRef};
use utils::MemoryPoolTransactionOutputProvider;
use VerificationParameters;
//// Block that is (possibly) partially verified.
#[derive(Debug)]
pub enum PartiallyVerifiedBlock {
/// Block that isn't verified at all.
NotVerified(IndexedBlock),
/// Block that has its header pre-verified (mind that AcceptHeader isn't called).
HeaderPreVerified(IndexedBlock),
}
/// Headers verification events sink
pub trait HeadersVerificationSink : Send + Sync + 'static {
/// When headers verification has completed successfully.
@ -48,7 +57,7 @@ pub enum VerificationTask {
/// Verify headers
VerifyHeaders(PeerIndex, Vec<IndexedBlockHeader>),
/// Verify single block
VerifyBlock(IndexedBlock),
VerifyBlock(PartiallyVerifiedBlock),
/// Verify single transaction
VerifyTransaction(BlockHeight, IndexedTransaction),
/// Stop verification thread
@ -62,7 +71,7 @@ pub trait Verifier : Send + Sync + 'static {
/// Verify headers
fn verify_headers(&self, peer: PeerIndex, headers: Vec<IndexedBlockHeader>);
/// Verify block
fn verify_block(&self, block: IndexedBlock);
fn verify_block(&self, block: PartiallyVerifiedBlock);
/// Verify transaction
fn verify_transaction(&self, height: BlockHeight, transaction: IndexedTransaction);
}
@ -87,6 +96,32 @@ pub struct ChainVerifierWrapper {
pub enforce_full_verification: AtomicBool,
}
impl PartiallyVerifiedBlock {
/// Returns hash of the block.
pub fn hash(&self) -> &H256 {
match *self {
PartiallyVerifiedBlock::NotVerified(ref block)
| PartiallyVerifiedBlock::HeaderPreVerified(ref block) => block.hash(),
}
}
}
impl From<PartiallyVerifiedBlock> for IndexedBlock {
fn from(block: PartiallyVerifiedBlock) -> Self {
match block {
PartiallyVerifiedBlock::NotVerified(block) => block,
PartiallyVerifiedBlock::HeaderPreVerified(block) => block,
}
}
}
#[cfg(test)]
impl From<IndexedBlock> for PartiallyVerifiedBlock {
fn from(block: IndexedBlock) -> Self {
PartiallyVerifiedBlock::NotVerified(block)
}
}
impl ChainVerifierWrapper {
/// Create new chain verifier wrapper.
pub fn new(verifier: Arc<ChainVerifier>, storage: &StorageRef, verification_params: VerificationParameters) -> Self {
@ -104,19 +139,30 @@ impl ChainVerifierWrapper {
}
/// Verify block.
pub fn verify_block(&self, block: &IndexedBlock) -> Result<(), VerificationError> {
pub fn verify_block(&self, block: &PartiallyVerifiedBlock) -> Result<(), VerificationError> {
let enforce_full_verification = if block.hash() == &self.verification_params.verification_edge {
self.enforce_full_verification.store(true, Ordering::Relaxed);
true
} else {
self.enforce_full_verification.load(Ordering::Relaxed)
};
let verification_level = if enforce_full_verification {
VerificationLevel::Full
// select base verification level
let mut verification_level = if enforce_full_verification {
VerificationLevel::FULL
} else {
self.verification_params.verification_level
};
// update verification level with hints, if necessary
let block = match *block {
PartiallyVerifiedBlock::NotVerified(ref block) => block,
PartiallyVerifiedBlock::HeaderPreVerified(ref block) => {
verification_level.insert(VerificationLevel::HINT_HEADER_PRE_VERIFIED);
block
},
};
self.verifier.verify(verification_level, block)
}
}
@ -210,7 +256,7 @@ impl AsyncVerifier {
// verify block
match verifier.verify_block(&block) {
Ok(_) => {
if let Some(tasks) = sink.on_block_verification_success(block) {
if let Some(tasks) = sink.on_block_verification_success(block.into()) {
tasks_queue.extend(tasks);
}
},
@ -267,7 +313,7 @@ impl Verifier for AsyncVerifier {
.expect("Verification thread have the same lifetime as `AsyncVerifier`");
}
fn verify_block(&self, block: IndexedBlock) {
fn verify_block(&self, block: PartiallyVerifiedBlock) {
self.verification_work_sender.lock()
.send(VerificationTask::VerifyBlock(block))
.expect("Verification thread have the same lifetime as `AsyncVerifier`");
@ -311,13 +357,13 @@ impl<T> Verifier for SyncVerifier<T> where T: VerificationSink {
}
/// Verify block
fn verify_block(&self, block: IndexedBlock) {
fn verify_block(&self, block: PartiallyVerifiedBlock) {
match self.verifier.verify_block(&block) {
Ok(_) => {
// SyncVerifier is used for bulk blocks import only
// => there is no memory pool
// => we could ignore decanonized transactions
self.sink.on_block_verification_success(block);
self.sink.on_block_verification_success(block.into());
},
Err(e) => self.sink.on_block_verification_error(&format!("{:?}", e), block.hash()),
}
@ -345,7 +391,7 @@ pub mod tests {
use primitives::hash::H256;
use chain::{IndexedBlockHeader, IndexedBlock, IndexedTransaction};
use super::{Verifier, HeadersVerificationSink, BlockVerificationSink, TransactionVerificationSink,
AsyncVerifier, VerificationTask, ChainVerifierWrapper};
AsyncVerifier, VerificationTask, ChainVerifierWrapper, PartiallyVerifiedBlock};
use types::{PeerIndex, BlockHeight, StorageRef, MemoryPoolRef};
use VerificationParameters;
@ -374,7 +420,7 @@ pub mod tests {
pub fn set_verifier(&mut self, verifier: Arc<ChainVerifier>) {
self.verifier = Some(ChainVerifierWrapper::new(verifier, self.storage.as_ref().unwrap(), VerificationParameters {
verification_level: VerificationLevel::Full,
verification_level: VerificationLevel::FULL,
verification_edge: 0u8.into(),
}));
}
@ -400,7 +446,7 @@ pub mod tests {
}
}
fn verify_block(&self, block: IndexedBlock) {
fn verify_block(&self, block: PartiallyVerifiedBlock) {
match self.sink {
Some(ref sink) => match self.errors.get(&block.hash()) {
Some(err) => sink.on_block_verification_error(&err, &block.hash()),
@ -408,7 +454,7 @@ pub mod tests {
if self.actual_checks.contains(block.hash()) {
AsyncVerifier::execute_single_task(sink, self.storage.as_ref().unwrap(), self.memory_pool.as_ref().unwrap(), self.verifier.as_ref().unwrap(), VerificationTask::VerifyBlock(block));
} else {
sink.on_block_verification_success(block);
sink.on_block_verification_success(block.into());
}
},
},
@ -441,18 +487,18 @@ pub mod tests {
// switching to full verification when block is already in db
assert_eq!(ChainVerifierWrapper::new(verifier.clone(), &storage, VerificationParameters {
verification_level: VerificationLevel::NoVerification,
verification_level: VerificationLevel::NO_VERIFICATION,
verification_edge: test_data::genesis().hash(),
}).enforce_full_verification.load(Ordering::Relaxed), true);
// switching to full verification when block with given hash is coming
let wrapper = ChainVerifierWrapper::new(verifier, &storage, VerificationParameters {
verification_level: VerificationLevel::NoVerification,
verification_level: VerificationLevel::NO_VERIFICATION,
verification_edge: test_data::block_h1().hash(),
});
assert_eq!(wrapper.enforce_full_verification.load(Ordering::Relaxed), false);
let block: IndexedBlock = test_data::block_h1().into();
let _ = wrapper.verify_block(&block);
let _ = wrapper.verify_block(&block.into());
assert_eq!(wrapper.enforce_full_verification.load(Ordering::Relaxed), true);
}
@ -502,17 +548,17 @@ pub mod tests {
// Ok(()) when tx script is not checked
let wrapper = ChainVerifierWrapper::new(verifier.clone(), &storage, VerificationParameters {
verification_level: VerificationLevel::Header,
verification_level: VerificationLevel::HEADER,
verification_edge: 1.into(),
});
assert_eq!(wrapper.verify_block(&bad_transaction_block), Ok(()));
assert_eq!(wrapper.verify_block(&bad_transaction_block.clone().into()), Ok(()));
// Error when tx script is checked
let wrapper = ChainVerifierWrapper::new(verifier, &storage, VerificationParameters {
verification_level: VerificationLevel::Full,
verification_level: VerificationLevel::FULL,
verification_edge: 1.into(),
});
assert_eq!(wrapper.verify_block(&bad_transaction_block), Err(VerificationError::Transaction(1, TransactionError::Signature(0, ScriptError::InvalidStackOperation))));
assert_eq!(wrapper.verify_block(&bad_transaction_block.into()), Err(VerificationError::Transaction(1, TransactionError::Signature(0, ScriptError::InvalidStackOperation))));
}
#[test]
@ -523,16 +569,16 @@ pub mod tests {
// Ok(()) when nothing is verified
let wrapper = ChainVerifierWrapper::new(verifier.clone(), &storage, VerificationParameters {
verification_level: VerificationLevel::NoVerification,
verification_level: VerificationLevel::NO_VERIFICATION,
verification_edge: 1.into(),
});
assert_eq!(wrapper.verify_block(&bad_block), Ok(()));
assert_eq!(wrapper.verify_block(&bad_block.clone().into()), Ok(()));
// Error when everything is verified
let wrapper = ChainVerifierWrapper::new(verifier, &storage, VerificationParameters {
verification_level: VerificationLevel::Full,
verification_level: VerificationLevel::FULL,
verification_edge: 1.into(),
});
assert_eq!(wrapper.verify_block(&bad_block), Err(VerificationError::Empty));
assert_eq!(wrapper.verify_block(&bad_block.into()), Err(VerificationError::Empty));
}
}

View File

@ -74,13 +74,14 @@ impl BestHeadersChain {
}
/// Insert new block header
pub fn insert(&mut self, header: IndexedBlockHeader) {
pub fn insert(&mut self, header: IndexedBlockHeader) -> bool {
// append to the best chain
if self.best_block_hash() == header.raw.previous_header_hash {
let header_hash = header.hash.clone();
self.headers.insert(header_hash.clone(), header);
self.best.push_back(header_hash);
return;
self.headers.insert(header_hash.clone(), header).is_some()
} else {
self.headers.contains_key(&header.hash)
}
}
@ -228,4 +229,13 @@ mod tests {
assert_eq!(chain.information().best, 1);
assert_eq!(chain.information().total, 1);
}
#[test]
fn insert_to_best_chain_returns_true_if_header_is_in_chain() {
let b0 = test_data::block_builder().header().build().build();
let b1 = test_data::block_builder().header().parent(b0.hash()).build().build().block_header;
let mut chain = BestHeadersChain::new(b0.hash());
assert!(!chain.insert(b1.clone().into()));
assert!(chain.insert(b1.clone().into()));
}
}

View File

@ -18,6 +18,7 @@ network = { path = "../network" }
storage = { path = "../storage" }
bitcrypto = { path = "../crypto" }
rustc-hex = "2"
bitflags = "1.0"
[dev-dependencies]
rand = "0.4"

View File

@ -124,7 +124,7 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
overspent: TransactionOverspent::new(transaction, output_store),
sigops: TransactionSigops::new(transaction, output_store, consensus, max_block_sigops, time),
double_spent: TransactionDoubleSpend::new(transaction, output_store),
eval: TransactionEval::new(transaction, output_store, consensus, VerificationLevel::Full, height, time, deployments),
eval: TransactionEval::new(transaction, output_store, consensus, VerificationLevel::FULL, height, time, deployments),
join_split: JoinSplitVerification::new(transaction, nullifier_tracker),
sapling: SaplingVerification::new(
nullifier_tracker,
@ -402,8 +402,7 @@ impl<'a> TransactionEval<'a> {
false => Default::default(),
};
if self.verification_level == VerificationLevel::Header
|| self.verification_level == VerificationLevel::NoVerification {
if self.verification_level.intersects(VerificationLevel::HEADER | VerificationLevel::NO_VERIFICATION) {
return Ok(no_input_sighash);
}

View File

@ -30,13 +30,13 @@ impl BackwardsCompatibleChainVerifier {
}
fn verify_block(&self, verification_level: VerificationLevel, block: &IndexedBlock) -> Result<(), Error> {
if verification_level == VerificationLevel::NoVerification {
if verification_level.intersects(VerificationLevel::NO_VERIFICATION) {
return Ok(());
}
let current_time = ::time::get_time().sec as u32;
// first run pre-verification
let chain_verifier = ChainVerifier::new(block, &self.consensus, current_time);
let chain_verifier = ChainVerifier::new(block, &self.consensus, current_time, verification_level);
chain_verifier.check()?;
assert_eq!(Some(self.store.best_block().hash), self.store.block_hash(self.store.best_block().number));
@ -153,7 +153,7 @@ mod tests {
let storage = Arc::new(BlockChainDatabase::init_test_chain(vec![test_data::genesis().into()]));
let b2 = test_data::block_h2().into();
let verifier = ChainVerifier::new(storage, ConsensusParams::new(Network::Unitest));
assert_eq!(Err(Error::Database(DBError::UnknownParent)), verifier.verify(VerificationLevel::Full, &b2));
assert_eq!(Err(Error::Database(DBError::UnknownParent)), verifier.verify(VerificationLevel::FULL, &b2));
}
#[test]
@ -161,7 +161,7 @@ mod tests {
let storage = Arc::new(BlockChainDatabase::init_test_chain(vec![test_data::genesis().into()]));
let b1 = test_data::block_h1();
let verifier = ChainVerifier::new(storage, ConsensusParams::new(Network::Mainnet));
assert_eq!(verifier.verify(VerificationLevel::Full, &b1.into()), Ok(()));
assert_eq!(verifier.verify(VerificationLevel::FULL, &b1.into()), Ok(()));
}
#[test]
@ -173,7 +173,7 @@ mod tests {
]);
let b1 = test_data::block_h2();
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Mainnet));
assert_eq!(verifier.verify(VerificationLevel::Full, &b1.into()), Ok(()));
assert_eq!(verifier.verify(VerificationLevel::FULL, &b1.into()), Ok(()));
}
#[test]
@ -210,7 +210,7 @@ mod tests {
TransactionError::Maturity,
));
assert_eq!(expected, verifier.verify(VerificationLevel::Full, &block.into()));
assert_eq!(expected, verifier.verify(VerificationLevel::FULL, &block.into()));
}
#[test]
@ -245,7 +245,7 @@ mod tests {
.build();
let verifier = ChainVerifier::new(Arc::new(storage), consensus);
assert_eq!(verifier.verify(VerificationLevel::Full, &block.into()), Ok(()));
assert_eq!(verifier.verify(VerificationLevel::FULL, &block.into()), Ok(()));
}
#[test]
@ -284,7 +284,7 @@ mod tests {
.build();
let verifier = ChainVerifier::new(Arc::new(storage), consensus);
assert!(verifier.verify(VerificationLevel::Full, &block.into()).is_ok());
assert!(verifier.verify(VerificationLevel::FULL, &block.into()).is_ok());
}
#[test]
@ -325,7 +325,7 @@ mod tests {
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
let expected = Err(Error::Transaction(2, TransactionError::Overspend));
assert_eq!(expected, verifier.verify(VerificationLevel::Full, &block.into()));
assert_eq!(expected, verifier.verify(VerificationLevel::FULL, &block.into()));
}
#[test]
@ -365,7 +365,7 @@ mod tests {
.build();
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
assert!(verifier.verify(VerificationLevel::Full, &block.into()).is_ok());
assert!(verifier.verify(VerificationLevel::FULL, &block.into()).is_ok());
}
#[test]
@ -413,7 +413,7 @@ mod tests {
let verifier = ChainVerifier::new(Arc::new(storage), ConsensusParams::new(Network::Unitest));
let expected = Err(Error::MaximumSigops);
assert_eq!(expected, verifier.verify(VerificationLevel::Full, &block.into()));
assert_eq!(expected, verifier.verify(VerificationLevel::FULL, &block.into()));
}
#[test]
@ -440,6 +440,6 @@ mod tests {
actual: 1250000001,
});
assert_eq!(expected, verifier.verify(VerificationLevel::Full, &block.into()));
assert_eq!(expected, verifier.verify(VerificationLevel::FULL, &block.into()));
}
}

View File

@ -60,6 +60,8 @@ extern crate byteorder;
#[cfg(test)]
extern crate rand;
extern crate rustc_hex as hex;
#[macro_use]
extern crate bitflags;
extern crate storage;
extern crate chain;
@ -122,15 +124,19 @@ pub use timestamp::{median_timestamp, median_timestamp_inclusive};
pub use work::{work_required, is_valid_proof_of_work, is_valid_proof_of_work_hash};
pub use deployments::Deployments;
#[derive(Debug, Clone, Copy, PartialEq)]
/// Blocks verification level.
pub enum VerificationLevel {
/// Full verification.
Full,
/// Transaction scripts are not checked.
Header,
/// No verification at all.
NoVerification,
bitflags! {
pub struct VerificationLevel: u32 {
/// Base level: perform full block verification.
const FULL = 0x00000001;
/// Base level: transaction scripts are not checked.
const HEADER = 0x00000002;
/// Base level: no blocks verification at all.
const NO_VERIFICATION = 0x00000004;
/// This bit is set if header pre-verification (non-context) has already been performed for the block.
const HINT_HEADER_PRE_VERIFIED = 0x10000000;
}
}
/// Interface for block verification

View File

@ -5,27 +5,39 @@ use error::Error;
use verify_block::BlockVerifier;
use verify_header::HeaderVerifier;
use verify_transaction::TransactionVerifier;
use VerificationLevel;
pub struct ChainVerifier<'a> {
pub block: BlockVerifier<'a>,
pub header: HeaderVerifier<'a>,
pub header: Option<HeaderVerifier<'a>>,
pub transactions: Vec<TransactionVerifier<'a>>,
}
impl<'a> ChainVerifier<'a> {
pub fn new(block: &'a IndexedBlock, consensus: &'a ConsensusParams, current_time: u32) -> Self {
pub fn new(
block: &'a IndexedBlock,
consensus: &'a ConsensusParams,
current_time: u32,
verification_level: VerificationLevel,
) -> Self {
trace!(target: "verification", "Block pre-verification {}", block.hash().to_reversed_str());
ChainVerifier {
block: BlockVerifier::new(block, consensus),
header: HeaderVerifier::new(&block.header, consensus, current_time),
header: if !verification_level.intersects(VerificationLevel::HINT_HEADER_PRE_VERIFIED) {
Some(HeaderVerifier::new(&block.header, consensus, current_time))
} else {
None
},
transactions: block.transactions.iter().map(|tx| TransactionVerifier::new(tx, consensus)).collect(),
}
}
pub fn check(&self) -> Result<(), Error> {
try!(self.block.check());
try!(self.header.check());
try!(self.check_transactions());
self.block.check()?;
if let Some(ref header) = self.header {
header.check()?;
}
self.check_transactions()?;
Ok(())
}