Merge pull request #222 from ethcore/verification_flags
immutable ChainVerifier
This commit is contained in:
commit
25748096fa
|
@ -52,6 +52,14 @@ pub enum BlockLocation {
|
||||||
Side(u32),
|
Side(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BlockLocation {
|
||||||
|
pub fn height(&self) -> u32 {
|
||||||
|
match *self {
|
||||||
|
BlockLocation::Main(h) | BlockLocation::Side(h) => h,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type SharedStore = std::sync::Arc<Store + Send + Sync>;
|
pub type SharedStore = std::sync::Arc<Store + Send + Sync>;
|
||||||
|
|
||||||
pub use best_block::BestBlock;
|
pub use best_block::BestBlock;
|
||||||
|
|
|
@ -3,9 +3,9 @@ use std::sync::Arc;
|
||||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use chain::Transaction;
|
use chain::Transaction;
|
||||||
use network::{Magic, ConsensusParams};
|
use network::Magic;
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use verification::{ChainVerifier, Verify as VerificationVerify};
|
use verification::{ChainVerifier, Verify as VerificationVerify, Chain};
|
||||||
use synchronization_chain::ChainRef;
|
use synchronization_chain::ChainRef;
|
||||||
use db::IndexedBlock;
|
use db::IndexedBlock;
|
||||||
|
|
||||||
|
@ -58,54 +58,25 @@ impl AsyncVerifier {
|
||||||
verification_worker_thread: Some(thread::Builder::new()
|
verification_worker_thread: Some(thread::Builder::new()
|
||||||
.name("Sync verification thread".to_string())
|
.name("Sync verification thread".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
AsyncVerifier::verification_worker_proc(sink, chain, network.consensus_params(), verifier, verification_work_receiver)
|
AsyncVerifier::verification_worker_proc(sink, verifier, verification_work_receiver)
|
||||||
})
|
})
|
||||||
.expect("Error creating verification thread"))
|
.expect("Error creating verification thread"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Thread procedure for handling verification tasks
|
/// Thread procedure for handling verification tasks
|
||||||
fn verification_worker_proc<T: VerificationSink>(sink: Arc<Mutex<T>>, chain: ChainRef, consensus_params: ConsensusParams, mut verifier: ChainVerifier, work_receiver: Receiver<VerificationTask>) {
|
fn verification_worker_proc<T: VerificationSink>(sink: Arc<Mutex<T>>, verifier: ChainVerifier, work_receiver: Receiver<VerificationTask>) {
|
||||||
let bip16_time_border = consensus_params.bip16_time;
|
|
||||||
let mut is_bip16_active = false;
|
|
||||||
let mut parameters_change_steps = Some(0);
|
|
||||||
|
|
||||||
while let Ok(task) = work_receiver.recv() {
|
while let Ok(task) = work_receiver.recv() {
|
||||||
match task {
|
match task {
|
||||||
VerificationTask::VerifyBlock(block) => {
|
VerificationTask::VerifyBlock(block) => {
|
||||||
// for changes that are not relying on block#
|
|
||||||
let is_bip16_active_on_block = block.header().time >= bip16_time_border;
|
|
||||||
let force_parameters_change = is_bip16_active_on_block != is_bip16_active;
|
|
||||||
if force_parameters_change {
|
|
||||||
parameters_change_steps = Some(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// change verifier parameters, if needed
|
|
||||||
if let Some(steps_left) = parameters_change_steps {
|
|
||||||
if steps_left == 0 {
|
|
||||||
let best_storage_block = chain.read().best_storage_block();
|
|
||||||
|
|
||||||
is_bip16_active = is_bip16_active_on_block;
|
|
||||||
verifier = verifier.verify_p2sh(is_bip16_active);
|
|
||||||
|
|
||||||
let is_bip65_active = best_storage_block.number >= consensus_params.bip65_height;
|
|
||||||
verifier = verifier.verify_clocktimeverify(is_bip65_active);
|
|
||||||
|
|
||||||
if is_bip65_active {
|
|
||||||
parameters_change_steps = None;
|
|
||||||
} else {
|
|
||||||
parameters_change_steps = Some(consensus_params.bip65_height - best_storage_block.number);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parameters_change_steps = Some(steps_left - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify block
|
// verify block
|
||||||
match verifier.verify(&block) {
|
match verifier.verify(&block) {
|
||||||
Ok(_chain) => {
|
Ok(Chain::Main) | Ok(Chain::Side) => {
|
||||||
sink.lock().on_block_verification_success(block)
|
sink.lock().on_block_verification_success(block)
|
||||||
},
|
},
|
||||||
|
Ok(Chain::Orphan) => {
|
||||||
|
unreachable!("sync will never put orphaned blocks to verification queue");
|
||||||
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
sink.lock().on_block_verification_error(&format!("{:?}", e), &block.hash())
|
sink.lock().on_block_verification_error(&format!("{:?}", e), &block.hash())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use db::{self, BlockLocation, PreviousTransactionOutputProvider};
|
use db::{self, BlockLocation, PreviousTransactionOutputProvider};
|
||||||
use network::Magic;
|
use network::{Magic, ConsensusParams};
|
||||||
use script::Script;
|
use script::Script;
|
||||||
use super::{Verify, VerificationResult, Chain, Error, TransactionError};
|
use super::{Verify, VerificationResult, Chain, Error, TransactionError};
|
||||||
use {chain, utils};
|
use {chain, utils};
|
||||||
|
@ -18,11 +18,10 @@ const TRANSACTIONS_VERIFY_PARALLEL_THRESHOLD: usize = 16;
|
||||||
|
|
||||||
pub struct ChainVerifier {
|
pub struct ChainVerifier {
|
||||||
store: db::SharedStore,
|
store: db::SharedStore,
|
||||||
verify_p2sh: bool,
|
|
||||||
verify_clocktimeverify: bool,
|
|
||||||
skip_pow: bool,
|
skip_pow: bool,
|
||||||
skip_sig: bool,
|
skip_sig: bool,
|
||||||
network: Magic,
|
network: Magic,
|
||||||
|
consensus_params: ConsensusParams,
|
||||||
pool: Pool,
|
pool: Pool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +29,10 @@ impl ChainVerifier {
|
||||||
pub fn new(store: db::SharedStore, network: Magic) -> Self {
|
pub fn new(store: db::SharedStore, network: Magic) -> Self {
|
||||||
ChainVerifier {
|
ChainVerifier {
|
||||||
store: store,
|
store: store,
|
||||||
verify_p2sh: false,
|
|
||||||
verify_clocktimeverify: false,
|
|
||||||
skip_pow: false,
|
skip_pow: false,
|
||||||
skip_sig: false,
|
skip_sig: false,
|
||||||
network: network,
|
network: network,
|
||||||
|
consensus_params: network.consensus_params(),
|
||||||
pool: Pool::new(TRANSACTIONS_VERIFY_THREADS),
|
pool: Pool::new(TRANSACTIONS_VERIFY_THREADS),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,14 +49,12 @@ impl ChainVerifier {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_p2sh(mut self, verify: bool) -> Self {
|
pub fn verify_p2sh(&self, time: u32) -> bool {
|
||||||
self.verify_p2sh = verify;
|
time >= self.consensus_params.bip16_time
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_clocktimeverify(mut self, verify: bool) -> Self {
|
pub fn verify_clocktimeverify(&self, height: u32) -> bool {
|
||||||
self.verify_clocktimeverify = verify;
|
height >= self.consensus_params.bip65_height
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns previous transaction output.
|
/// Returns previous transaction output.
|
||||||
|
@ -102,7 +98,7 @@ impl ChainVerifier {
|
||||||
fn block_sigops(&self, block: &db::IndexedBlock) -> usize {
|
fn block_sigops(&self, block: &db::IndexedBlock) -> usize {
|
||||||
// strict pay-to-script-hash signature operations count toward block
|
// strict pay-to-script-hash signature operations count toward block
|
||||||
// signature operations limit is enforced with BIP16
|
// signature operations limit is enforced with BIP16
|
||||||
let bip16_active = block.header().time >= self.network.consensus_params().bip16_time;
|
let bip16_active = self.verify_p2sh(block.header().time);
|
||||||
block.transactions().map(|(_, tx)| self.transaction_sigops(block, tx, bip16_active)).sum()
|
block.transactions().map(|(_, tx)| self.transaction_sigops(block, tx, bip16_active)).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +113,6 @@ impl ChainVerifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_hash = block.hash();
|
let block_hash = block.hash();
|
||||||
let consensus_params = self.network.consensus_params();
|
|
||||||
|
|
||||||
// check that difficulty matches the adjusted level
|
// check that difficulty matches the adjusted level
|
||||||
if let Some(work) = self.work_required(block, at_height) {
|
if let Some(work) = self.work_required(block, at_height) {
|
||||||
|
@ -137,7 +132,7 @@ impl ChainVerifier {
|
||||||
// bip30
|
// bip30
|
||||||
for (tx_index, (tx_hash, _)) in block.transactions().enumerate() {
|
for (tx_index, (tx_hash, _)) in block.transactions().enumerate() {
|
||||||
if let Some(meta) = self.store.transaction_meta(tx_hash) {
|
if let Some(meta) = self.store.transaction_meta(tx_hash) {
|
||||||
if !meta.is_fully_spent() && !consensus_params.is_bip30_exception(&block_hash, at_height) {
|
if !meta.is_fully_spent() && !self.consensus_params.is_bip30_exception(&block_hash, at_height) {
|
||||||
return Err(Error::Transaction(tx_index, TransactionError::UnspentTransactionWithTheSameHash));
|
return Err(Error::Transaction(tx_index, TransactionError::UnspentTransactionWithTheSameHash));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,10 +176,11 @@ impl ChainVerifier {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
//block: &db::IndexedBlock,
|
|
||||||
pub fn verify_transaction<T>(
|
pub fn verify_transaction<T>(
|
||||||
&self,
|
&self,
|
||||||
prevout_provider: &T,
|
prevout_provider: &T,
|
||||||
|
height: u32,
|
||||||
|
time: u32,
|
||||||
transaction: &chain::Transaction,
|
transaction: &chain::Transaction,
|
||||||
sequence: usize
|
sequence: usize
|
||||||
) -> Result<(), TransactionError> where T: PreviousTransactionOutputProvider {
|
) -> Result<(), TransactionError> where T: PreviousTransactionOutputProvider {
|
||||||
|
@ -220,8 +216,8 @@ impl ChainVerifier {
|
||||||
let output: Script = paired_output.script_pubkey.into();
|
let output: Script = paired_output.script_pubkey.into();
|
||||||
|
|
||||||
let flags = VerificationFlags::default()
|
let flags = VerificationFlags::default()
|
||||||
.verify_p2sh(self.verify_p2sh)
|
.verify_p2sh(self.verify_p2sh(time))
|
||||||
.verify_clocktimeverify(self.verify_clocktimeverify);
|
.verify_clocktimeverify(self.verify_clocktimeverify(height));
|
||||||
|
|
||||||
// for tests only, skips as late as possible
|
// for tests only, skips as late as possible
|
||||||
if self.skip_sig { continue; }
|
if self.skip_sig { continue; }
|
||||||
|
@ -292,6 +288,11 @@ impl ChainVerifier {
|
||||||
return Err(Error::CoinbaseSignatureLength(coinbase_script_len));
|
return Err(Error::CoinbaseSignatureLength(coinbase_script_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let location = match self.store.accepted_location(block.header()) {
|
||||||
|
Some(location) => location,
|
||||||
|
None => return Ok(Chain::Orphan),
|
||||||
|
};
|
||||||
|
|
||||||
if block.transaction_count() > TRANSACTIONS_VERIFY_PARALLEL_THRESHOLD {
|
if block.transaction_count() > TRANSACTIONS_VERIFY_PARALLEL_THRESHOLD {
|
||||||
// todo: might use on-stack vector (smallvec/elastic array)
|
// todo: might use on-stack vector (smallvec/elastic array)
|
||||||
let mut transaction_tasks: Vec<Task> = Vec::with_capacity(TRANSACTIONS_VERIFY_THREADS);
|
let mut transaction_tasks: Vec<Task> = Vec::with_capacity(TRANSACTIONS_VERIFY_THREADS);
|
||||||
|
@ -300,7 +301,7 @@ impl ChainVerifier {
|
||||||
let from = last;
|
let from = last;
|
||||||
last = ::std::cmp::max(1, block.transaction_count() / TRANSACTIONS_VERIFY_THREADS);
|
last = ::std::cmp::max(1, block.transaction_count() / TRANSACTIONS_VERIFY_THREADS);
|
||||||
if num_task == TRANSACTIONS_VERIFY_THREADS - 1 { last = block.transaction_count(); };
|
if num_task == TRANSACTIONS_VERIFY_THREADS - 1 { last = block.transaction_count(); };
|
||||||
transaction_tasks.push(Task::new(block, from, last));
|
transaction_tasks.push(Task::new(block, location.height(), from, last));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pool.scoped(|scope| {
|
self.pool.scoped(|scope| {
|
||||||
|
@ -318,22 +319,19 @@ impl ChainVerifier {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (index, (_, tx)) in block.transactions().enumerate() {
|
for (index, (_, tx)) in block.transactions().enumerate() {
|
||||||
if let Err(tx_err) = self.verify_transaction(block, tx, index) {
|
if let Err(tx_err) = self.verify_transaction(block, location.height(), block.header().time, tx, index) {
|
||||||
return Err(Error::Transaction(index, tx_err));
|
return Err(Error::Transaction(index, tx_err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: pre-process projected block number once verification is parallel!
|
// todo: pre-process projected block number once verification is parallel!
|
||||||
match self.store.accepted_location(block.header()) {
|
match location {
|
||||||
None => {
|
BlockLocation::Main(block_number) => {
|
||||||
Ok(Chain::Orphan)
|
|
||||||
},
|
|
||||||
Some(BlockLocation::Main(block_number)) => {
|
|
||||||
try!(self.ordered_verify(block, block_number));
|
try!(self.ordered_verify(block, block_number));
|
||||||
Ok(Chain::Main)
|
Ok(Chain::Main)
|
||||||
},
|
},
|
||||||
Some(BlockLocation::Side(block_number)) => {
|
BlockLocation::Side(block_number) => {
|
||||||
try!(self.ordered_verify(block, block_number));
|
try!(self.ordered_verify(block, block_number));
|
||||||
Ok(Chain::Side)
|
Ok(Chain::Side)
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,6 +4,7 @@ use db::IndexedBlock;
|
||||||
|
|
||||||
pub struct Task<'a> {
|
pub struct Task<'a> {
|
||||||
block: &'a IndexedBlock,
|
block: &'a IndexedBlock,
|
||||||
|
block_height: u32,
|
||||||
from: usize,
|
from: usize,
|
||||||
to: usize,
|
to: usize,
|
||||||
result: Result<(), TransactionCheckError>,
|
result: Result<(), TransactionCheckError>,
|
||||||
|
@ -12,9 +13,10 @@ pub struct Task<'a> {
|
||||||
type TransactionCheckError = (usize, TransactionError);
|
type TransactionCheckError = (usize, TransactionError);
|
||||||
|
|
||||||
impl<'a> Task<'a> {
|
impl<'a> Task<'a> {
|
||||||
pub fn new(block: &'a IndexedBlock, from: usize, to: usize) -> Self {
|
pub fn new(block: &'a IndexedBlock, block_height: u32, from: usize, to: usize) -> Self {
|
||||||
Task {
|
Task {
|
||||||
block: block,
|
block: block,
|
||||||
|
block_height: block_height,
|
||||||
from: from,
|
from: from,
|
||||||
to: to,
|
to: to,
|
||||||
result: Ok(()),
|
result: Ok(()),
|
||||||
|
@ -23,7 +25,7 @@ impl<'a> Task<'a> {
|
||||||
|
|
||||||
pub fn progress(&mut self, verifier: &ChainVerifier) {
|
pub fn progress(&mut self, verifier: &ChainVerifier) {
|
||||||
for index in self.from..self.to {
|
for index in self.from..self.to {
|
||||||
if let Err(e) = verifier.verify_transaction(self.block, self.block.transaction_at(index).1, index) {
|
if let Err(e) = verifier.verify_transaction(self.block, self.block_height, self.block.header().time, self.block.transaction_at(index).1, index) {
|
||||||
self.result = Err((index, e))
|
self.result = Err((index, e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue