verification: fix race same block + misc (#11400)
* ethcore: fix race in verification * verification: fix some nits * verification: refactor err type `Kind::create` * fix: tests * address grumbles * address grumbles: don't panic
This commit is contained in:
committed by
Andronik Ordian
parent
108daf1861
commit
a47a5b1992
@@ -62,7 +62,14 @@ pub trait Kind: 'static + Sized + Send + Sync {
|
||||
type Verified: Sized + Send + BlockLike + MallocSizeOf;
|
||||
|
||||
/// Attempt to create the `Unverified` item from the input.
|
||||
fn create(input: Self::Input, engine: &dyn Engine, check_seal: bool) -> Result<Self::Unverified, (Self::Input, Error)>;
|
||||
///
|
||||
/// The return type is quite complex because in some scenarios the input
|
||||
/// is needed (typically for BlockError) to get the raw block bytes without cloning them
|
||||
fn create(
|
||||
input: Self::Input,
|
||||
engine: &dyn Engine,
|
||||
check_seal: bool
|
||||
) -> Result<Self::Unverified, (Error, Option<Self::Input>)>;
|
||||
|
||||
/// Attempt to verify the `Unverified` item using the given engine.
|
||||
fn verify(unverified: Self::Unverified, engine: &dyn Engine, check_seal: bool) -> Result<Self::Verified, Error>;
|
||||
@@ -91,16 +98,20 @@ pub mod blocks {
|
||||
type Unverified = Unverified;
|
||||
type Verified = PreverifiedBlock;
|
||||
|
||||
fn create(input: Self::Input, engine: &dyn Engine, check_seal: bool) -> Result<Self::Unverified, (Self::Input, Error)> {
|
||||
fn create(
|
||||
input: Self::Input,
|
||||
engine: &dyn Engine,
|
||||
check_seal: bool
|
||||
) -> Result<Self::Unverified, (Error, Option<Self::Input>)> {
|
||||
match verify_block_basic(&input, engine, check_seal) {
|
||||
Ok(()) => Ok(input),
|
||||
Err(Error::Block(BlockError::TemporarilyInvalid(oob))) => {
|
||||
debug!(target: "client", "Block received too early {}: {:?}", input.hash(), oob);
|
||||
Err((input, BlockError::TemporarilyInvalid(oob).into()))
|
||||
Err((BlockError::TemporarilyInvalid(oob).into(), Some(input)))
|
||||
},
|
||||
Err(e) => {
|
||||
warn!(target: "client", "Stage 1 block verification failed for {}: {:?}", input.hash(), e);
|
||||
Err((input, e))
|
||||
Err((e, Some(input)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,11 +138,11 @@ pub mod blocks {
|
||||
}
|
||||
|
||||
fn parent_hash(&self) -> H256 {
|
||||
self.header.parent_hash().clone()
|
||||
*self.header.parent_hash()
|
||||
}
|
||||
|
||||
fn difficulty(&self) -> U256 {
|
||||
self.header.difficulty().clone()
|
||||
*self.header.difficulty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,11 +156,11 @@ pub mod blocks {
|
||||
}
|
||||
|
||||
fn parent_hash(&self) -> H256 {
|
||||
self.header.parent_hash().clone()
|
||||
*self.header.parent_hash()
|
||||
}
|
||||
|
||||
fn difficulty(&self) -> U256 {
|
||||
self.header.difficulty().clone()
|
||||
*self.header.difficulty()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,8 +181,8 @@ pub mod headers {
|
||||
impl BlockLike for Header {
|
||||
fn hash(&self) -> H256 { self.hash() }
|
||||
fn raw_hash(&self) -> H256 { self.hash() }
|
||||
fn parent_hash(&self) -> H256 { self.parent_hash().clone() }
|
||||
fn difficulty(&self) -> U256 { self.difficulty().clone() }
|
||||
fn parent_hash(&self) -> H256 { *self.parent_hash() }
|
||||
fn difficulty(&self) -> U256 { *self.difficulty() }
|
||||
}
|
||||
|
||||
/// A mode for verifying headers.
|
||||
@@ -182,19 +193,23 @@ pub mod headers {
|
||||
type Unverified = Header;
|
||||
type Verified = Header;
|
||||
|
||||
fn create(input: Self::Input, engine: &dyn Engine, check_seal: bool) -> Result<Self::Unverified, (Self::Input, Error)> {
|
||||
fn create(
|
||||
input: Self::Input,
|
||||
engine: &dyn Engine,
|
||||
check_seal: bool
|
||||
) -> Result<Self::Unverified, (Error, Option<Self::Input>)> {
|
||||
let res = verify_header_params(&input, engine, check_seal)
|
||||
.and_then(|_| verify_header_time(&input));
|
||||
|
||||
match res {
|
||||
Ok(_) => Ok(input),
|
||||
Err(err) => Err((input, err))
|
||||
Err(e) => Err((e, Some(input))),
|
||||
}
|
||||
}
|
||||
|
||||
fn verify(unverified: Self::Unverified, engine: &dyn Engine, check_seal: bool) -> Result<Self::Verified, Error> {
|
||||
match check_seal {
|
||||
true => engine.verify_block_unordered(&unverified,).map(|_| unverified),
|
||||
true => engine.verify_block_unordered(&unverified).map(|_| unverified),
|
||||
false => Ok(unverified),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,30 +467,33 @@ impl<K: Kind, C> VerificationQueue<K, C> {
|
||||
}
|
||||
|
||||
/// Add a block to the queue.
|
||||
pub fn import(&self, input: K::Input) -> Result<H256, (K::Input, Error)> {
|
||||
//
|
||||
// TODO: #11403 - rework `EthcoreError::Block` to include raw bytes of the error cause
|
||||
pub fn import(&self, input: K::Input) -> Result<H256, (Error, Option<K::Input>)> {
|
||||
let hash = input.hash();
|
||||
let raw_hash = input.raw_hash();
|
||||
{
|
||||
if self.processing.read().contains_key(&hash) {
|
||||
return Err((input, Error::Import(ImportError::AlreadyQueued).into()));
|
||||
return Err((Error::Import(ImportError::AlreadyQueued), Some(input)));
|
||||
}
|
||||
|
||||
let mut bad = self.verification.bad.lock();
|
||||
if bad.contains(&hash) || bad.contains(&raw_hash) {
|
||||
return Err((input, Error::Import(ImportError::KnownBad).into()));
|
||||
return Err((Error::Import(ImportError::KnownBad), Some(input)));
|
||||
}
|
||||
|
||||
if bad.contains(&input.parent_hash()) {
|
||||
bad.insert(hash);
|
||||
return Err((input, Error::Import(ImportError::KnownBad).into()));
|
||||
return Err((Error::Import(ImportError::KnownBad), Some(input)));
|
||||
}
|
||||
}
|
||||
|
||||
match K::create(input, &*self.engine, self.verification.check_seal) {
|
||||
Ok(item) => {
|
||||
if self.processing.write().insert(hash, item.difficulty()).is_some() {
|
||||
return Err((Error::Import(ImportError::AlreadyQueued), None));
|
||||
}
|
||||
self.verification.sizes.unverified.fetch_add(item.malloc_size_of(), AtomicOrdering::SeqCst);
|
||||
|
||||
self.processing.write().insert(hash, item.difficulty());
|
||||
{
|
||||
let mut td = self.total_difficulty.write();
|
||||
*td = *td + item.difficulty();
|
||||
@@ -499,7 +502,7 @@ impl<K: Kind, C> VerificationQueue<K, C> {
|
||||
self.more_to_verify.notify_all();
|
||||
Ok(hash)
|
||||
},
|
||||
Err((input, err)) => {
|
||||
Err((err, input)) => {
|
||||
match err {
|
||||
// Don't mark future blocks as bad.
|
||||
Error::Block(BlockError::TemporarilyInvalid(_)) => {},
|
||||
@@ -517,7 +520,7 @@ impl<K: Kind, C> VerificationQueue<K, C> {
|
||||
self.verification.bad.lock().insert(hash);
|
||||
}
|
||||
}
|
||||
Err((input, err))
|
||||
Err((err, input))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -582,7 +585,7 @@ impl<K: Kind, C> VerificationQueue<K, C> {
|
||||
let count = cmp::min(max, verified.len());
|
||||
let result = verified.drain(..count).collect::<Vec<_>>();
|
||||
|
||||
let drained_size = result.iter().map(MallocSizeOfExt::malloc_size_of).fold(0, |a, c| a + c);
|
||||
let drained_size = result.iter().map(MallocSizeOfExt::malloc_size_of).sum();
|
||||
self.verification.sizes.verified.fetch_sub(drained_size, AtomicOrdering::SeqCst);
|
||||
|
||||
self.ready_signal.reset();
|
||||
@@ -636,7 +639,7 @@ impl<K: Kind, C> VerificationQueue<K, C> {
|
||||
|
||||
/// Get the total difficulty of all the blocks in the queue.
|
||||
pub fn total_difficulty(&self) -> U256 {
|
||||
self.total_difficulty.read().clone()
|
||||
*self.total_difficulty.read()
|
||||
}
|
||||
|
||||
/// Get the current number of working verifiers.
|
||||
@@ -806,9 +809,9 @@ mod tests {
|
||||
|
||||
let duplicate_import = queue.import(new_unverified(get_good_dummy_block()));
|
||||
match duplicate_import {
|
||||
Err((_, e)) => {
|
||||
Err(e) => {
|
||||
match e {
|
||||
EthcoreError::Import(ImportError::AlreadyQueued) => {},
|
||||
(EthcoreError::Import(ImportError::AlreadyQueued), _) => {},
|
||||
_ => { panic!("must return AlreadyQueued error"); }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user