Fix pubsub new_blocks notifications to include all blocks (#9987)

Fix: new blocks notifications sometimes missing in pubsub RPC

Implement new struct to pass to `new_blocks()` with extra parameter - `has_more_blocks_to_import`, which was previously used to determine whether the notification should be sent. Now it's up to each implementation to decide what to do.

Updated all implementations to behave as before, except `eth_pubsub`, which will send notification even when the queue is not empty.

Update tests.
This commit is contained in:
mattrutherford
2018-12-19 09:24:14 +00:00
committed by GitHub
parent 13b832f959
commit 215602de08
14 changed files with 145 additions and 136 deletions

View File

@@ -114,19 +114,51 @@ impl ChainRoute {
}
}
/// Used by `ChainNotify` `new_blocks()`
pub struct NewBlocks {
/// Imported blocks
pub imported: Vec<H256>,
/// Invalid blocks
pub invalid: Vec<H256>,
/// Route
pub route: ChainRoute,
/// Sealed
pub sealed: Vec<H256>,
/// Block bytes.
pub proposed: Vec<Bytes>,
/// Duration
pub duration: Duration,
/// Has more blocks to import
pub has_more_blocks_to_import: bool,
}
impl NewBlocks {
/// Constructor
pub fn new (
imported: Vec<H256>,
invalid: Vec<H256>,
route: ChainRoute,
sealed: Vec<H256>,
proposed: Vec<Bytes>,
duration: Duration,
has_more_blocks_to_import: bool,
) -> NewBlocks {
NewBlocks {
imported,
invalid,
route,
sealed,
proposed,
duration,
has_more_blocks_to_import,
}
}
}
/// Represents what has to be handled by actor listening to chain events
pub trait ChainNotify : Send + Sync {
/// fires when chain has new blocks.
fn new_blocks(
&self,
_imported: Vec<H256>,
_invalid: Vec<H256>,
_route: ChainRoute,
_sealed: Vec<H256>,
// Block bytes.
_proposed: Vec<Bytes>,
_duration: Duration,
) {
fn new_blocks( &self, _new_blocks: NewBlocks) {
// does nothing by default
}

View File

@@ -44,7 +44,7 @@ use client::{
use client::{
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
TraceFilter, CallAnalytics, Mode,
ChainNotify, ChainRoute, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType,
ChainNotify, NewBlocks, ChainRoute, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType,
IoClient, BadBlocks,
};
use client::bad_blocks;
@@ -268,7 +268,7 @@ impl Importer {
}
let max_blocks_to_import = client.config.max_round_blocks_to_import;
let (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration, is_empty) = {
let (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration, has_more_blocks_to_import) = {
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
let mut invalid_blocks = HashSet::new();
let mut proposed_blocks = Vec::with_capacity(max_blocks_to_import);
@@ -322,26 +322,29 @@ impl Importer {
if !invalid_blocks.is_empty() {
self.block_queue.mark_as_bad(&invalid_blocks);
}
let is_empty = self.block_queue.mark_as_good(&imported_blocks);
(imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, start.elapsed(), is_empty)
let has_more_blocks_to_import = !self.block_queue.mark_as_good(&imported_blocks);
(imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, start.elapsed(), has_more_blocks_to_import)
};
{
if !imported_blocks.is_empty() && is_empty {
if !imported_blocks.is_empty() {
let route = ChainRoute::from(import_results.as_ref());
if is_empty {
if !has_more_blocks_to_import {
self.miner.chain_new_blocks(client, &imported_blocks, &invalid_blocks, route.enacted(), route.retracted(), false);
}
client.notify(|notify| {
notify.new_blocks(
imported_blocks.clone(),
invalid_blocks.clone(),
route.clone(),
Vec::new(),
proposed_blocks.clone(),
duration,
NewBlocks::new(
imported_blocks.clone(),
invalid_blocks.clone(),
route.clone(),
Vec::new(),
proposed_blocks.clone(),
duration,
has_more_blocks_to_import,
)
);
});
}
@@ -2342,12 +2345,15 @@ impl ImportSealedBlock for Client {
);
self.notify(|notify| {
notify.new_blocks(
vec![hash],
vec![],
route.clone(),
vec![hash],
vec![],
start.elapsed(),
NewBlocks::new(
vec![hash],
vec![],
route.clone(),
vec![hash],
vec![],
start.elapsed(),
false
)
);
});
self.db.read().key_value().flush().expect("DB flush failed.");
@@ -2360,12 +2366,15 @@ impl BroadcastProposalBlock for Client {
const DURATION_ZERO: Duration = Duration::from_millis(0);
self.notify(|notify| {
notify.new_blocks(
vec![],
vec![],
ChainRoute::default(),
vec![],
vec![block.rlp_bytes()],
DURATION_ZERO,
NewBlocks::new(
vec![],
vec![],
ChainRoute::default(),
vec![],
vec![block.rlp_bytes()],
DURATION_ZERO,
false
)
);
});
}

View File

@@ -34,7 +34,7 @@ pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactResult};
pub use self::io_message::ClientIoMessage;
#[cfg(any(test, feature = "test-helpers"))]
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use self::chain_notify::{ChainNotify, ChainRoute, ChainRouteType, ChainMessageType};
pub use self::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType, ChainMessageType};
pub use self::traits::{
Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, CallContract, TransactionInfo, RegistryInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock,
StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks,

View File

@@ -17,14 +17,13 @@
//! Watcher for snapshot-related chain events.
use parking_lot::Mutex;
use client::{BlockInfo, Client, ChainNotify, ChainRoute, ClientIoMessage};
use client::{BlockInfo, Client, ChainNotify, NewBlocks, ClientIoMessage};
use ids::BlockId;
use io::IoChannel;
use ethereum_types::H256;
use bytes::Bytes;
use std::{sync::Arc, time::Duration};
use std::sync::Arc;
// helper trait for transforming hashes to numbers and checking if syncing.
trait Oracle: Send + Sync {
@@ -99,20 +98,12 @@ impl Watcher {
}
impl ChainNotify for Watcher {
fn new_blocks(
&self,
imported: Vec<H256>,
_: Vec<H256>,
_: ChainRoute,
_: Vec<H256>,
_: Vec<Bytes>,
_duration: Duration)
{
if self.oracle.is_major_importing() { return }
fn new_blocks(&self, new_blocks: NewBlocks) {
if self.oracle.is_major_importing() || new_blocks.has_more_blocks_to_import { return }
trace!(target: "snapshot_watcher", "{} imported", imported.len());
trace!(target: "snapshot_watcher", "{} imported", new_blocks.imported.len());
let highest = imported.into_iter()
let highest = new_blocks.imported.into_iter()
.filter_map(|h| self.oracle.to_number(h))
.filter(|&num| num >= self.period + self.history)
.map(|num| num - self.history)
@@ -130,7 +121,7 @@ impl ChainNotify for Watcher {
mod tests {
use super::{Broadcast, Oracle, Watcher};
use client::{ChainNotify, ChainRoute};
use client::{ChainNotify, NewBlocks, ChainRoute};
use ethereum_types::{H256, U256};
@@ -170,14 +161,15 @@ mod tests {
history: history,
};
watcher.new_blocks(
watcher.new_blocks(NewBlocks::new(
hashes,
vec![],
ChainRoute::default(),
vec![],
vec![],
DURATION_ZERO,
);
false
));
}
// helper