do not verify headers twice
This commit is contained in:
parent
f69e0a00a1
commit
2626366c7c
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
},
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ network = { path = "../network" }
|
|||
storage = { path = "../storage" }
|
||||
bitcrypto = { path = "../crypto" }
|
||||
rustc-hex = "2"
|
||||
bitflags = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.4"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue