Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.

Commit 63e2781

Browse files
authored
Fix issues during block sync (#11265)
1 parent f81c576 commit 63e2781

File tree

11 files changed

+197
-44
lines changed

11 files changed

+197
-44
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ethcore/client-traits/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,9 @@ pub trait BlockChainClient:
401401
/// Schedule state-altering transaction to be executed on the next pending
402402
/// block with the given gas and nonce parameters.
403403
fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error>;
404+
405+
/// Returns true, if underlying import queue is processing possible fork at the moment
406+
fn is_processing_fork(&self) -> bool;
404407
}
405408

406409
/// The data required for a `Client` to create a transaction.

ethcore/src/client/client.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,11 @@ impl BlockChainClient for Client {
17641764
}
17651765
}
17661766

1767+
fn is_processing_fork(&self) -> bool {
1768+
let chain = self.chain.read();
1769+
self.importer.block_queue.is_processing_fork(&chain.best_block_hash(), &chain)
1770+
}
1771+
17671772
fn block_total_difficulty(&self, id: BlockId) -> Option<U256> {
17681773
let chain = self.chain.read();
17691774

ethcore/src/test_helpers/test_client.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,8 @@ impl BlockChainClient for TestBlockChainClient {
810810
}
811811
}
812812

813+
fn is_processing_fork(&self) -> bool { false }
814+
813815
// works only if blocks are one after another 1 -> 2 -> 3
814816
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
815817
Some(TreeRoute {

ethcore/sync/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ bytes = { package = "parity-bytes", version = "0.1" }
1313
client-traits = { path = "../client-traits" }
1414
common-types = { path = "../types" }
1515
devp2p = { package = "ethcore-network-devp2p", path = "../../util/network-devp2p" }
16+
derive_more = "0.99"
1617
enum-primitive-derive = "0.2"
1718
ethcore-io = { path = "../../util/io" }
1819
ethcore-private-tx = { path = "../private-tx" }

ethcore/sync/src/api.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ const MAINTAIN_SYNC_TIMER: TimerToken = 1;
466466
const CONTINUE_SYNC_TIMER: TimerToken = 2;
467467
const TX_TIMER: TimerToken = 3;
468468
const PRIORITY_TIMER: TimerToken = 4;
469+
const DELAYED_PROCESSING_TIMER: TimerToken = 5;
469470

470471
pub(crate) const PRIORITY_TIMER_INTERVAL: Duration = Duration::from_millis(250);
471472

@@ -489,6 +490,7 @@ impl NetworkProtocolHandler for SyncProtocolHandler {
489490
io.register_timer(MAINTAIN_SYNC_TIMER, Duration::from_millis(1100)).expect("Error registering sync timer");
490491
io.register_timer(CONTINUE_SYNC_TIMER, Duration::from_millis(2500)).expect("Error registering sync timer");
491492
io.register_timer(TX_TIMER, Duration::from_millis(1300)).expect("Error registering transactions timer");
493+
io.register_timer(DELAYED_PROCESSING_TIMER, Duration::from_millis(2100)).expect("Error registering delayed processing timer");
492494

493495
io.register_timer(PRIORITY_TIMER, PRIORITY_TIMER_INTERVAL).expect("Error registering peers timer");
494496
}
@@ -539,6 +541,7 @@ impl NetworkProtocolHandler for SyncProtocolHandler {
539541
CONTINUE_SYNC_TIMER => self.sync.write().continue_sync(&mut io),
540542
TX_TIMER => self.sync.write().propagate_new_transactions(&mut io),
541543
PRIORITY_TIMER => self.sync.process_priority_queue(&mut io),
544+
DELAYED_PROCESSING_TIMER => self.sync.process_delayed_requests(&mut io),
542545
_ => warn!("Unknown timer {} triggered.", timer),
543546
}
544547
}

ethcore/sync/src/block_sync.rs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,19 @@ impl BlockDownloader {
231231
self.state = State::Blocks;
232232
}
233233

234+
/// Reset sync to the specified block
235+
fn reset_to_block(&mut self, start_hash: &H256, start_number: BlockNumber) {
236+
self.reset();
237+
self.last_imported_block = start_number;
238+
self.last_imported_hash = start_hash.clone();
239+
self.last_round_start = start_number;
240+
self.last_round_start_hash = start_hash.clone();
241+
self.imported_this_round = None;
242+
self.round_parents = VecDeque::new();
243+
self.target_hash = None;
244+
self.retract_step = 1;
245+
}
246+
234247
/// Returns best imported block number.
235248
pub fn last_imported_block_number(&self) -> BlockNumber {
236249
self.last_imported_block
@@ -439,22 +452,28 @@ impl BlockDownloader {
439452
trace_sync!(self, "Searching common header from the last round {} ({})", self.last_imported_block, self.last_imported_hash);
440453
} else {
441454
let best = io.chain().chain_info().best_block_number;
455+
let best_hash = io.chain().chain_info().best_block_hash;
442456
let oldest_reorg = io.chain().pruning_info().earliest_state;
443457
if self.block_set == BlockSet::NewBlocks && best > start && start < oldest_reorg {
444458
debug_sync!(self, "Could not revert to previous ancient block, last: {} ({})", start, start_hash);
445-
self.reset();
459+
self.reset_to_block(&best_hash, best);
446460
} else {
447461
let n = start - cmp::min(self.retract_step, start);
448-
self.retract_step *= 2;
449-
match io.chain().block_hash(BlockId::Number(n)) {
450-
Some(h) => {
451-
self.last_imported_block = n;
452-
self.last_imported_hash = h;
453-
trace_sync!(self, "Searching common header in the blockchain {} ({})", start, self.last_imported_hash);
454-
}
455-
None => {
456-
debug_sync!(self, "Could not revert to previous block, last: {} ({})", start, self.last_imported_hash);
457-
self.reset();
462+
if n == 0 {
463+
debug_sync!(self, "Header not found, bottom line reached, resetting, last imported: {}", self.last_imported_hash);
464+
self.reset_to_block(&best_hash, best);
465+
} else {
466+
self.retract_step *= 2;
467+
match io.chain().block_hash(BlockId::Number(n)) {
468+
Some(h) => {
469+
self.last_imported_block = n;
470+
self.last_imported_hash = h;
471+
trace_sync!(self, "Searching common header in the blockchain {} ({})", start, self.last_imported_hash);
472+
}
473+
None => {
474+
debug_sync!(self, "Could not revert to previous block, last: {} ({})", start, self.last_imported_hash);
475+
self.reset_to_block(&best_hash, best);
476+
}
458477
}
459478
}
460479
}

ethcore/sync/src/chain/handler.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::{
3131
SnapshotDataPacket, SnapshotManifestPacket, StatusPacket,
3232
}
3333
},
34-
BlockSet, ChainSync, ForkConfirmation, PacketDecodeError, PeerAsking, PeerInfo, SyncRequester,
34+
BlockSet, ChainSync, ForkConfirmation, PacketProcessError, PeerAsking, PeerInfo, SyncRequester,
3535
SyncState, ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, MAX_NEW_BLOCK_AGE, MAX_NEW_HASHES,
3636
PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4,
3737
}
@@ -114,6 +114,7 @@ impl SyncHandler {
114114
debug!(target: "sync", "Disconnected {}", peer_id);
115115
sync.clear_peer_download(peer_id);
116116
sync.peers.remove(&peer_id);
117+
sync.delayed_requests.retain(|(request_peer_id, _, _)| *request_peer_id != peer_id);
117118
sync.active_peers.remove(&peer_id);
118119

119120
if sync.state == SyncState::SnapshotManifest {
@@ -149,23 +150,27 @@ impl SyncHandler {
149150
trace!(target: "sync", "Ignoring new block from unconfirmed peer {}", peer_id);
150151
return Ok(());
151152
}
152-
let difficulty: U256 = r.val_at(1)?;
153-
if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) {
154-
if peer.difficulty.map_or(true, |pd| difficulty > pd) {
155-
peer.difficulty = Some(difficulty);
156-
}
157-
}
158153
let block = Unverified::from_rlp(r.at(0)?.as_raw().to_vec())?;
159154
let hash = block.header.hash();
160155
let number = block.header.number();
161156
trace!(target: "sync", "{} -> NewBlock ({})", peer_id, hash);
162157
if number > sync.highest_block.unwrap_or(0) {
163158
sync.highest_block = Some(number);
164159
}
160+
let parent_hash = block.header.parent_hash();
161+
let difficulty: U256 = r.val_at(1)?;
162+
// Most probably the sent block is being imported by peer right now
163+
// Use td and hash, that peer must have for now
164+
let parent_td = difficulty.checked_sub(*block.header.difficulty());
165+
if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) {
166+
if peer.difficulty.map_or(true, |pd| parent_td.map_or(false, |td| td > pd)) {
167+
peer.difficulty = parent_td;
168+
}
169+
}
165170
let mut unknown = false;
166171

167172
if let Some(ref mut peer) = sync.peers.get_mut(&peer_id) {
168-
peer.latest_hash = hash;
173+
peer.latest_hash = *parent_hash;
169174
}
170175

171176
let last_imported_number = sync.new_blocks.last_imported_block_number();
@@ -675,7 +680,7 @@ impl SyncHandler {
675680
}
676681

677682
/// Called when peer sends us new transactions
678-
pub fn on_peer_transactions(sync: &ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, tx_rlp: Rlp) -> Result<(), PacketDecodeError> {
683+
pub fn on_peer_transactions(sync: &ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, tx_rlp: Rlp) -> Result<(), PacketProcessError> {
679684
// Accept transactions only when fully synced
680685
if !io.is_chain_queue_empty() || (sync.state != SyncState::Idle && sync.state != SyncState::NewBlocks) {
681686
trace!(target: "sync", "{} Ignoring transactions while syncing", peer_id);

ethcore/sync/src/chain/mod.rs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,14 @@ use crate::{
113113

114114
use bytes::Bytes;
115115
use client_traits::BlockChainClient;
116+
use derive_more::Display;
116117
use ethereum_types::{H256, U256};
117118
use fastmap::{H256FastMap, H256FastSet};
118119
use futures::sync::mpsc as futures_mpsc;
119120
use keccak_hash::keccak;
120121
use log::{error, trace, debug, warn};
121122
use network::client_version::ClientVersion;
122-
use network::{self, PeerId, PacketId};
123+
use network::{self, PeerId};
123124
use parity_util_mem::{MallocSizeOfExt, malloc_size_of_is_0};
124125
use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
125126
use rand::{Rng, seq::SliceRandom};
@@ -147,7 +148,23 @@ pub(crate) use self::supplier::SyncSupplier;
147148

148149
malloc_size_of_is_0!(PeerInfo);
149150

150-
pub type PacketDecodeError = DecoderError;
151+
/// Possible errors during packet's processing
152+
#[derive(Debug, Display)]
153+
pub enum PacketProcessError {
154+
/// Error of RLP decoder
155+
#[display(fmt = "Decoder Error: {}", _0)]
156+
Decoder(DecoderError),
157+
/// Underlying client is busy and cannot process the packet
158+
/// The packet should be postponed for later response
159+
#[display(fmt = "Underlying client is busy")]
160+
ClientBusy,
161+
}
162+
163+
impl From<DecoderError> for PacketProcessError {
164+
fn from(err: DecoderError) -> Self {
165+
PacketProcessError::Decoder(err).into()
166+
}
167+
}
151168

152169
/// Version 64 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count).
153170
pub const ETH_PROTOCOL_VERSION_64: (u8, u8) = (64, 0x11);
@@ -411,7 +428,7 @@ pub mod random {
411428
}
412429
}
413430

414-
pub type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
431+
pub type RlpResponseResult = Result<Option<(SyncPacket, RlpStream)>, PacketProcessError>;
415432
pub type Peers = HashMap<PeerId, PeerInfo>;
416433

417434
/// Thread-safe wrapper for `ChainSync`.
@@ -468,6 +485,17 @@ impl ChainSyncApi {
468485
SyncSupplier::dispatch_packet(&self.sync, io, peer, packet_id, data)
469486
}
470487

488+
/// Process the queue with requests, that were delayed with response.
489+
pub fn process_delayed_requests(&self, io: &mut dyn SyncIo) {
490+
let requests = self.sync.write().retrieve_delayed_requests();
491+
if !requests.is_empty() {
492+
debug!(target: "sync", "Processing {} delayed requests", requests.len());
493+
for (peer_id, packet_id, packet_data) in requests {
494+
SyncSupplier::dispatch_delayed_request(&self.sync, io, peer_id, packet_id, &packet_data);
495+
}
496+
}
497+
}
498+
471499
/// Process a priority propagation queue.
472500
/// This task is run from a timer and should be time constrained.
473501
/// Hence we set up a deadline for the execution and cancel the task if the deadline is exceeded.
@@ -672,6 +700,10 @@ pub struct ChainSync {
672700
/// Connected peers pending Status message.
673701
/// Value is request timestamp.
674702
handshaking_peers: HashMap<PeerId, Instant>,
703+
/// Requests, that can not be processed at the moment
704+
delayed_requests: Vec<(PeerId, u8, Vec<u8>)>,
705+
/// Ids of delayed requests, used for lookup, id is composed from peer id and packet id
706+
delayed_requests_ids: HashSet<(PeerId, u8)>,
675707
/// Sync start timestamp. Measured when first peer is connected
676708
sync_start_time: Option<Instant>,
677709
/// Transactions propagation statistics
@@ -707,6 +739,8 @@ impl ChainSync {
707739
peers: HashMap::new(),
708740
handshaking_peers: HashMap::new(),
709741
active_peers: HashSet::new(),
742+
delayed_requests: Vec::new(),
743+
delayed_requests_ids: HashSet::new(),
710744
new_blocks: BlockDownloader::new(BlockSet::NewBlocks, &chain_info.best_block_hash, chain_info.best_block_number),
711745
old_blocks: None,
712746
last_sent_block_number: 0,
@@ -821,6 +855,22 @@ impl ChainSync {
821855
self.active_peers = self.peers.keys().cloned().collect();
822856
}
823857

858+
/// Add a request for later processing
859+
pub fn add_delayed_request(&mut self, peer: PeerId, packet_id: u8, data: &[u8]) {
860+
// Ignore the request, if there is a request already in queue with the same id
861+
if !self.delayed_requests_ids.contains(&(peer, packet_id)) {
862+
self.delayed_requests_ids.insert((peer, packet_id));
863+
self.delayed_requests.push((peer, packet_id, data.to_vec()));
864+
debug!(target: "sync", "Delayed request with packet id {} from peer {} added", packet_id, peer);
865+
}
866+
}
867+
868+
/// Drain and return all delayed requests
869+
pub fn retrieve_delayed_requests(&mut self) -> Vec<(PeerId, u8, Vec<u8>)> {
870+
self.delayed_requests_ids.clear();
871+
self.delayed_requests.drain(..).collect()
872+
}
873+
824874
/// Restart sync
825875
pub fn reset_and_continue(&mut self, io: &mut dyn SyncIo) {
826876
trace!(target: "sync", "Restarting");
@@ -1261,7 +1311,7 @@ impl ChainSync {
12611311
packet.append(&chain.total_difficulty);
12621312
packet.append(&chain.best_block_hash);
12631313
packet.append(&chain.genesis_hash);
1264-
if eth_protocol_version >= ETH_PROTOCOL_VERSION_64.0 {
1314+
if eth_protocol_version >= ETH_PROTOCOL_VERSION_64.0 {
12651315
packet.append(&self.fork_filter.current(io.chain()));
12661316
}
12671317
if warp_protocol {

0 commit comments

Comments
 (0)