From d398b848025fbedf05238f831ee65a071b99c7c3 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Wed, 3 Feb 2016 23:42:30 +0300 Subject: [PATCH] remerge of 264 --- sync/src/chain.rs | 47 ++++++++++++++++++++++++----- sync/src/tests.rs | 77 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 109 insertions(+), 15 deletions(-) diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 15068a47e..e351ad837 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -15,12 +15,14 @@ use util::*; use std::mem::{replace}; -use views::{HeaderView}; -use header::{BlockNumber, Header as BlockHeader}; -use client::{BlockChainClient, BlockStatus}; -use sync::range_collection::{RangeCollection, ToUsize, FromUsize}; -use error::*; -use sync::io::SyncIo; +use ethcore::views::{HeaderView}; +use ethcore::header::{BlockNumber, Header as BlockHeader}; +use ethcore::client::{BlockChainClient, BlockStatus}; +use range_collection::{RangeCollection, ToUsize, FromUsize}; +use ethcore::error::*; +use ethcore::block::Block; +use io::SyncIo; +use time; use std::option::Option; impl ToUsize for BlockNumber { @@ -61,6 +63,8 @@ const RECEIPTS_PACKET: u8 = 0x10; const NETWORK_ID: U256 = ONE_U256; //TODO: get this from parent +const CONNECTION_TIMEOUT_SEC: f64 = 30f64; + struct Header { /// Header data data: Bytes, @@ -138,6 +142,8 @@ struct PeerInfo { asking: PeerAsking, /// A set of block numbers being requested asking_blocks: Vec, + /// Request timestamp + ask_time: f64, } /// Blockchain sync handler. @@ -250,6 +256,7 @@ impl ChainSync { genesis: try!(r.val_at(4)), asking: PeerAsking::Nothing, asking_blocks: Vec::new(), + ask_time: 0f64, }; trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest, peer.genesis); @@ -409,6 +416,7 @@ impl ChainSync { trace!(target: "sync", "{} -> NewBlock ({})", peer_id, h); let header_view = HeaderView::new(header_rlp.as_raw()); + let mut unknown = false; // TODO: Decompose block and add to self.headers and self.bodies instead if header_view.number() == From::from(self.current_base_block() + 1) { match io.chain().import_block(block_rlp.as_raw().to_vec()) { @@ -421,13 +429,20 @@ impl ChainSync { Ok(_) => { trace!(target: "sync", "New block queued {:?}", h); }, + Err(ImportError::UnknownParent) => { + unknown = true; + trace!(target: "sync", "New block with unknown parent {:?}", h); + }, Err(e) => { debug!(target: "sync", "Bad new block {:?} : {:?}", h, e); io.disable_peer(peer_id); } }; } - else { + else { + unknown = true; + } + if unknown { trace!(target: "sync", "New block unknown {:?}", h); //TODO: handle too many unknown blocks let difficulty: U256 = try!(r.val_at(1)); @@ -676,6 +691,14 @@ impl ChainSync { block_rlp.append_raw(body.at(0).as_raw(), 1); block_rlp.append_raw(body.at(1).as_raw(), 1); let h = &headers.1[i].hash; + + // Perform basic block verification + if !Block::is_good(block_rlp.as_raw()) { + debug!(target: "sync", "Bad block rlp {:?} : {:?}", h, block_rlp.as_raw()); + restart = true; + break; + } + match io.chain().import_block(block_rlp.out()) { Err(ImportError::AlreadyInChain) => { trace!(target: "sync", "Block already in chain {:?}", h); @@ -795,6 +818,7 @@ impl ChainSync { Ok(_) => { let mut peer = self.peers.get_mut(&peer_id).unwrap(); peer.asking = asking; + peer.ask_time = time::precise_time_s(); } } } @@ -972,4 +996,13 @@ impl ChainSync { /// Maintain other peers. Send out any new blocks and transactions pub fn _maintain_sync(&mut self, _io: &mut SyncIo) { } + + pub fn maintain_peers(&self, io: &mut SyncIo) { + let tick = time::precise_time_s(); + for (peer_id, peer) in &self.peers { + if peer.asking != PeerAsking::Nothing && (tick - peer.ask_time) > CONNECTION_TIMEOUT_SEC { + io.disconnect_peer(*peer_id); + } + } + } } diff --git a/sync/src/tests.rs b/sync/src/tests.rs index 5b796e6f1..ccf9cacf1 100644 --- a/sync/src/tests.rs +++ b/sync/src/tests.rs @@ -4,7 +4,7 @@ use ethcore::block_queue::BlockQueueInfo; use ethcore::header::{Header as BlockHeader, BlockNumber}; use ethcore::error::*; use io::SyncIo; -use chain::ChainSync; +use chain::{ChainSync, SyncState}; struct TestBlockChainClient { blocks: RwLock>, @@ -248,13 +248,15 @@ struct TestPeer { } struct TestNet { - peers: Vec + peers: Vec, + started: bool, } impl TestNet { pub fn new(n: usize) -> TestNet { let mut net = TestNet { peers: Vec::new(), + started: false, }; for _ in 0..n { net.peers.push(TestPeer { @@ -298,10 +300,28 @@ impl TestNet { } } - pub fn sync(&mut self) { + pub fn restart_peer(&mut self, i: usize) { + let peer = self.peer_mut(i); + peer.sync.restart(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None)); + } + + pub fn sync(&mut self) -> u32 { self.start(); + let mut total_steps = 0; while !self.done() { - self.sync_step() + self.sync_step(); + total_steps = total_steps + 1; + } + total_steps + } + + pub fn sync_steps(&mut self, count: usize) { + if !self.started { + self.start(); + self.started = true; + } + for _ in 0..count { + self.sync_step(); } } @@ -310,9 +330,8 @@ impl TestNet { } } - #[test] -fn full_sync_two_peers() { +fn chain_two_peers() { ::env_logger::init().ok(); let mut net = TestNet::new(3); net.peer_mut(1).chain.add_blocks(1000, false); @@ -323,7 +342,27 @@ fn full_sync_two_peers() { } #[test] -fn full_sync_empty_blocks() { +fn chain_status_after_sync() { + ::env_logger::init().ok(); + let mut net = TestNet::new(3); + net.peer_mut(1).chain.add_blocks(1000, false); + net.peer_mut(2).chain.add_blocks(1000, false); + net.sync(); + let status = net.peer(0).sync.status(); + assert_eq!(status.state, SyncState::Idle); +} + +#[test] +fn chain_takes_few_steps() { + let mut net = TestNet::new(3); + net.peer_mut(1).chain.add_blocks(100, false); + net.peer_mut(2).chain.add_blocks(100, false); + let total_steps = net.sync(); + assert!(total_steps < 7); +} + +#[test] +fn chain_empty_blocks() { ::env_logger::init().ok(); let mut net = TestNet::new(3); for n in 0..200 { @@ -336,7 +375,7 @@ fn full_sync_empty_blocks() { } #[test] -fn forked_sync() { +fn chain_forged() { ::env_logger::init().ok(); let mut net = TestNet::new(3); net.peer_mut(0).chain.add_blocks(300, false); @@ -354,3 +393,25 @@ fn forked_sync() { assert_eq!(net.peer(1).chain.numbers.read().unwrap().deref(), &peer1_chain); assert_eq!(net.peer(2).chain.numbers.read().unwrap().deref(), &peer1_chain); } + +#[test] +fn chain_restart() { + let mut net = TestNet::new(3); + net.peer_mut(1).chain.add_blocks(1000, false); + net.peer_mut(2).chain.add_blocks(1000, false); + + net.sync_steps(8); + + // make sure that sync has actually happened + assert!(net.peer(0).chain.chain_info().best_block_number > 100); + net.restart_peer(0); + + let status = net.peer(0).sync.status(); + assert_eq!(status.state, SyncState::NotSynced); +} + +#[test] +fn chain_status_empty() { + let net = TestNet::new(2); + assert_eq!(net.peer(0).sync.status().state, SyncState::NotSynced); +} \ No newline at end of file