From 35cabecad885997ffa328b6b123de5a8ae12dfb8 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 23 Feb 2016 18:51:29 +0100 Subject: [PATCH 01/14] support for polling --- Cargo.lock | 9 ++ ethcore/src/filter.rs | 16 ++++ rpc/Cargo.toml | 1 + rpc/src/lib.rs | 1 + rpc/src/v1/helpers/mod.rs | 21 +++++ rpc/src/v1/helpers/poll_filter.rs | 10 ++ rpc/src/v1/helpers/poll_indexer.rs | 144 +++++++++++++++++++++++++++++ rpc/src/v1/impls/eth.rs | 73 +++++++++++++-- rpc/src/v1/mod.rs | 1 + 9 files changed, 267 insertions(+), 9 deletions(-) create mode 100644 rpc/src/v1/helpers/mod.rs create mode 100644 rpc/src/v1/helpers/poll_filter.rs create mode 100644 rpc/src/v1/helpers/poll_indexer.rs diff --git a/Cargo.lock b/Cargo.lock index 779220fb6..fe1122195 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,6 +211,7 @@ dependencies = [ "serde_codegen 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "transient-hashmap 0.1.0 (git+https://github.com/debris/transient-hashmap)", ] [[package]] @@ -728,6 +729,14 @@ name = "traitobject" version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "transient-hashmap" +version = "0.1.0" +source = "git+https://github.com/debris/transient-hashmap#e21bf844277785504ddc30ee22d2a709103d4992" +dependencies = [ + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typeable" version = "0.1.2" diff --git a/ethcore/src/filter.rs b/ethcore/src/filter.rs index f5f9135d6..73aea8ccc 100644 --- a/ethcore/src/filter.rs +++ b/ethcore/src/filter.rs @@ -42,6 +42,22 @@ pub struct Filter { pub topics: [Option>; 4], } +impl Clone for Filter { + fn clone(&self) -> Self { + let mut topics = [None, None, None, None]; + for i in 0..4 { + topics[i] = self.topics[i].clone(); + } + + Filter { + from_block: self.from_block.clone(), + to_block: self.to_block.clone(), + address: self.address.clone(), + topics: topics + } + } +} + impl Filter { /// Returns combinations of each address and topic. pub fn bloom_possibilities(&self) -> Vec { diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index e36048690..03cffbb85 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -19,6 +19,7 @@ ethsync = { path = "../sync" } clippy = { version = "0.0.44", optional = true } rustc-serialize = "0.3" serde_macros = { version = "0.6.13", optional = true } +transient-hashmap = { git = "https://github.com/debris/transient-hashmap" } [build-dependencies] serde_codegen = { version = "0.6.13", optional = true } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index d7b5bdc3b..70d6fae82 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -27,6 +27,7 @@ extern crate jsonrpc_http_server; extern crate ethcore_util as util; extern crate ethcore; extern crate ethsync; +extern crate transient_hashmap; #[cfg(feature = "serde_macros")] include!("lib.rs.in"); diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs new file mode 100644 index 000000000..d212d74ab --- /dev/null +++ b/rpc/src/v1/helpers/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +mod poll_indexer; +mod poll_filter; + +pub use self::poll_indexer::PollIndexer; +pub use self::poll_filter::PollFilter; diff --git a/rpc/src/v1/helpers/poll_filter.rs b/rpc/src/v1/helpers/poll_filter.rs new file mode 100644 index 000000000..465290270 --- /dev/null +++ b/rpc/src/v1/helpers/poll_filter.rs @@ -0,0 +1,10 @@ +//! Helper type with all filter possibilities. + +use ethcore::filter::Filter; + +#[derive(Clone)] +pub enum PollFilter { + Block, + PendingTransaction, + Logs(Filter) +} diff --git a/rpc/src/v1/helpers/poll_indexer.rs b/rpc/src/v1/helpers/poll_indexer.rs new file mode 100644 index 000000000..11cb30935 --- /dev/null +++ b/rpc/src/v1/helpers/poll_indexer.rs @@ -0,0 +1,144 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Indexes all rpc poll requests. + +use transient_hashmap::{TransientHashMap, Timer, StandardTimer}; + +/// Lifetime of poll (in seconds). +const POLL_LIFETIME: u64 = 60; + +pub type PollId = usize; +pub type BlockNumber = u64; + +pub struct PollInfo { + pub filter: F, + pub block_number: BlockNumber +} + +impl Clone for PollInfo where F: Clone { + fn clone(&self) -> Self { + PollInfo { + filter: self.filter.clone(), + block_number: self.block_number.clone() + } + } +} + +/// Indexes all poll requests. +/// +/// Lazily garbage collects unused polls info. +pub struct PollIndexer where T: Timer { + polls: TransientHashMap, T>, + next_available_id: PollId +} + +impl PollIndexer { + /// Creates new instance of indexer. + pub fn new() -> Self { + PollIndexer::new_with_timer(Default::default()) + } +} + +impl PollIndexer where T: Timer { + pub fn new_with_timer(timer: T) -> Self { + PollIndexer { + polls: TransientHashMap::new_with_timer(POLL_LIFETIME, timer), + next_available_id: 0 + } + } + + /// Returns id which can be used for new poll. + /// + /// Stores information when last poll happend. + pub fn create_poll(&mut self, filter: F, block: BlockNumber) -> PollId { + self.polls.prune(); + let id = self.next_available_id; + self.next_available_id += 1; + self.polls.insert(id, PollInfo { + filter: filter, + block_number: block + }); + id + } + + /// Updates information when last poll happend. + pub fn update_poll(&mut self, id: &PollId, block: BlockNumber) { + self.polls.prune(); + if let Some(info) = self.polls.get_mut(id) { + info.block_number = block; + } + } + + /// Returns number of block when last poll happend. + pub fn get_poll(&mut self, id: &PollId) -> Option<&PollInfo> { + self.polls.prune(); + self.polls.get(id) + } + + /// Removes poll info. + pub fn remove_poll(&mut self, id: &PollId) { + self.polls.remove(id); + } +} + +#[cfg(test)] +mod tests { + use std::cell::RefCell; + use transient_hashmap::Timer; + use v1::helpers::PollIndexer; + + struct TestTimer<'a> { + time: &'a RefCell + } + + impl<'a> Timer for TestTimer<'a> { + fn get_time(&self) -> i64 { + *self.time.borrow() + } + } + + #[test] + fn test_poll_indexer() { + let time = RefCell::new(0); + let timer = TestTimer { + time: &time + }; + + let mut indexer = PollIndexer::new_with_timer(timer); + assert_eq!(indexer.create_poll(false, 20), 0); + assert_eq!(indexer.create_poll(true, 20), 1); + + *time.borrow_mut() = 10; + indexer.update_poll(&0, 21); + assert_eq!(indexer.get_poll(&0).unwrap().filter, false); + assert_eq!(indexer.get_poll(&0).unwrap().block_number, 21); + + *time.borrow_mut() = 30; + indexer.update_poll(&1, 23); + assert_eq!(indexer.get_poll(&1).unwrap().filter, true); + assert_eq!(indexer.get_poll(&1).unwrap().block_number, 23); + + *time.borrow_mut() = 75; + indexer.update_poll(&0, 30); + assert!(indexer.get_poll(&0).is_none()); + assert_eq!(indexer.get_poll(&1).unwrap().filter, true); + assert_eq!(indexer.get_poll(&1).unwrap().block_number, 23); + + indexer.remove_poll(&1); + assert!(indexer.get_poll(&1).is_none()); + } +} diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 689afd019..153a51216 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . //! Eth rpc implementation. -use std::sync::Arc; +use std::sync::{Mutex, Arc}; use ethsync::{EthSync, SyncState}; use jsonrpc_core::*; use util::hash::*; @@ -26,6 +26,7 @@ use ethcore::views::*; use ethcore::ethereum::denominations::shannon; use v1::traits::{Eth, EthFilter}; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, OptionalValue, Index, Filter, Log}; +use v1::helpers::{PollFilter, PollIndexer}; /// Eth rpc implementation. pub struct EthClient { @@ -212,28 +213,82 @@ impl Eth for EthClient { /// Eth filter rpc implementation. pub struct EthFilterClient { - client: Arc + client: Arc, + polls: Mutex> } impl EthFilterClient { /// Creates new Eth filter client. pub fn new(client: Arc) -> Self { EthFilterClient { - client: client + client: client, + polls: Mutex::new(PollIndexer::new()) } } } impl EthFilter for EthFilterClient { - fn new_block_filter(&self, _params: Params) -> Result { - Ok(Value::U64(0)) + fn new_filter(&self, params: Params) -> Result { + from_params::<(Filter,)>(params) + .and_then(|(filter,)| { + let mut polls = self.polls.lock().unwrap(); + let id = polls.create_poll(PollFilter::Logs(filter.into()), self.client.chain_info().best_block_number); + to_value(&U256::from(id)) + }) } - fn new_pending_transaction_filter(&self, _params: Params) -> Result { - Ok(Value::U64(1)) + fn new_block_filter(&self, params: Params) -> Result { + match params { + Params::None => { + let mut polls = self.polls.lock().unwrap(); + let id = polls.create_poll(PollFilter::Block, self.client.chain_info().best_block_number); + to_value(&U256::from(id)) + }, + _ => Err(Error::invalid_params()) + } } - fn filter_changes(&self, _: Params) -> Result { - to_value(&self.client.chain_info().best_block_hash).map(|v| Value::Array(vec![v])) + fn new_pending_transaction_filter(&self, params: Params) -> Result { + match params { + Params::None => { + let mut polls = self.polls.lock().unwrap(); + let id = polls.create_poll(PollFilter::PendingTransaction, self.client.chain_info().best_block_number); + to_value(&U256::from(id)) + }, + _ => Err(Error::invalid_params()) + } + } + + fn filter_changes(&self, params: Params) -> Result { + from_params::<(Index,)>(params) + .and_then(|(index,)| { + let info = self.polls.lock().unwrap().get_poll(&index.value()).cloned(); + match info { + None => Ok(Value::Array(vec![] as Vec)), + Some(info) => match info.filter { + PollFilter::Block => { + //unimplemented!() + to_value(&self.client.chain_info().best_block_hash).map(|v| Value::Array(vec![v])) + }, + PollFilter::PendingTransaction => { + //unimplemented!() + to_value(&self.client.chain_info().best_block_hash).map(|v| Value::Array(vec![v])) + }, + PollFilter::Logs(mut filter) => { + filter.from_block = BlockId::Number(info.block_number); + filter.to_block = BlockId::Latest; + let logs = self.client.logs(filter) + .into_iter() + .map(From::from) + .collect::>(); + + let current_number = self.client.chain_info().best_block_number; + self.polls.lock().unwrap().update_poll(&index.value(), current_number); + + to_value(&logs) + } + } + } + }) } } diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index 01635e872..57f7a944a 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -23,6 +23,7 @@ mod impls; mod types; #[cfg(test)] mod tests; +mod helpers; pub use self::traits::{Web3, Eth, EthFilter, Net}; pub use self::impls::*; From 0318907fb39e6535a69524e0a6ec419063695c83 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 24 Feb 2016 14:16:05 +0100 Subject: [PATCH 02/14] rpc eth_getFilterChanges returns new blocks, implemented eth_uninstallFilter --- ethcore/src/client.rs | 8 ++++++++ rpc/src/v1/impls/eth.rs | 21 ++++++++++++++++++--- sync/src/tests/helpers.rs | 4 ++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/ethcore/src/client.rs b/ethcore/src/client.rs index 9244c26fc..3a1a80141 100644 --- a/ethcore/src/client.rs +++ b/ethcore/src/client.rs @@ -114,6 +114,9 @@ pub trait BlockChainClient : Sync + Send { /// Get block total difficulty. fn block_total_difficulty(&self, id: BlockId) -> Option; + /// Get block hash. + fn block_hash(&self, id: BlockId) -> Option; + /// Get address code. fn code(&self, address: &Address) -> Option; @@ -416,6 +419,11 @@ impl BlockChainClient for Client { Self::block_hash(&chain, id).and_then(|hash| chain.block_details(&hash)).map(|d| d.total_difficulty) } + fn block_hash(&self, id: BlockId) -> Option { + let chain = self.chain.read().unwrap(); + Self::block_hash(&chain, id) + } + fn code(&self, address: &Address) -> Option { self.state().code(address) } diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 153a51216..e2cc1a5c3 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -267,11 +267,18 @@ impl EthFilter for EthFilterClient { None => Ok(Value::Array(vec![] as Vec)), Some(info) => match info.filter { PollFilter::Block => { - //unimplemented!() - to_value(&self.client.chain_info().best_block_hash).map(|v| Value::Array(vec![v])) + let current_number = self.client.chain_info().best_block_number; + let hashes = (info.block_number..current_number).into_iter() + .map(BlockId::Number) + .filter_map(|id| self.client.block_hash(id)) + .collect::>(); + + self.polls.lock().unwrap().update_poll(&index.value(), current_number); + + to_value(&hashes) }, PollFilter::PendingTransaction => { - //unimplemented!() + // TODO: fix implementation to_value(&self.client.chain_info().best_block_hash).map(|v| Value::Array(vec![v])) }, PollFilter::Logs(mut filter) => { @@ -291,4 +298,12 @@ impl EthFilter for EthFilterClient { } }) } + + fn uninstall_filter(&self, params: Params) -> Result { + from_params::<(Index,)>(params) + .and_then(|(index,)| { + self.polls.lock().unwrap().remove_poll(&index.value()); + to_value(&true) + }) + } } diff --git a/sync/src/tests/helpers.rs b/sync/src/tests/helpers.rs index 7f2928ccd..1d442f908 100644 --- a/sync/src/tests/helpers.rs +++ b/sync/src/tests/helpers.rs @@ -105,6 +105,10 @@ impl BlockChainClient for TestBlockChainClient { Some(U256::zero()) } + fn block_hash(&self, id: BlockId) -> Option { + unimplemented!(); + } + fn code(&self, _address: &Address) -> Option { unimplemented!(); } From b60b84fe67f1b77aee61e7c1f41c96f427c1c28b Mon Sep 17 00:00:00 2001 From: debris Date: Sat, 27 Feb 2016 15:56:41 +0100 Subject: [PATCH 03/14] use transient-hashmap from crates.io --- Cargo.lock | 4 ++-- rpc/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 124e3911e..622ac2e54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -212,7 +212,7 @@ dependencies = [ "serde_codegen 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "transient-hashmap 0.1.0 (git+https://github.com/debris/transient-hashmap)", + "transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -795,7 +795,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "transient-hashmap" version = "0.1.0" -source = "git+https://github.com/debris/transient-hashmap#e21bf844277785504ddc30ee22d2a709103d4992" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index edc749180..3c9851ed3 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -19,7 +19,7 @@ ethsync = { path = "../sync" } clippy = { version = "0.0.44", optional = true } rustc-serialize = "0.3" serde_macros = { version = "0.6.13", optional = true } -transient-hashmap = { git = "https://github.com/debris/transient-hashmap" } +transient-hashmap = "0.1" [build-dependencies] serde_codegen = { version = "0.6.13", optional = true } From 30e7ac8d6d4a9f0f6553d48bedd82083266aaa7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 1 Mar 2016 21:54:53 +0100 Subject: [PATCH 04/14] Fixing trivial warnings --- util/src/table.rs | 2 +- util/src/uint.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/util/src/table.rs b/util/src/table.rs index f04a498f8..e41209608 100644 --- a/util/src/table.rs +++ b/util/src/table.rs @@ -111,7 +111,7 @@ impl Table /// /// Returns previous value (if any) pub fn insert(&mut self, row: Row, col: Col, val: Val) -> Option { - self.map.entry(row).or_insert_with(|| HashMap::new()).insert(col, val) + self.map.entry(row).or_insert_with(HashMap::new).insert(col, val) } } diff --git a/util/src/uint.rs b/util/src/uint.rs index 88256d5f2..068456334 100644 --- a/util/src/uint.rs +++ b/util/src/uint.rs @@ -1982,6 +1982,7 @@ mod tests { #[test] + #[cfg_attr(feature = "dev", allow(cyclomatic_complexity))] fn u256_multi_full_mul() { let result = U256([0, 0, 0, 0]).full_mul(U256([0, 0, 0, 0])); assert_eq!(U512([0, 0, 0, 0, 0, 0, 0, 0]), result); From 083747dc674c63739cdde254ca9731e2fb784343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 1 Mar 2016 22:03:29 +0100 Subject: [PATCH 05/14] Lowering complexity of request_blocks --- sync/src/chain.rs | 159 ++++++++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 77 deletions(-) diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 6a7add27f..1d90e2d03 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -249,14 +249,14 @@ impl ChainSync { blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 }, num_peers: self.peers.len(), num_active_peers: self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(), - mem_used: + mem_used: // TODO: https://github.com/servo/heapsize/pull/50 - // self.downloading_hashes.heap_size_of_children() - //+ self.downloading_bodies.heap_size_of_children() - //+ self.downloading_hashes.heap_size_of_children() - self.headers.heap_size_of_children() - + self.bodies.heap_size_of_children() - + self.peers.heap_size_of_children() + // self.downloading_hashes.heap_size_of_children() + //+ self.downloading_bodies.heap_size_of_children() + //+ self.downloading_hashes.heap_size_of_children() + self.headers.heap_size_of_children() + + self.bodies.heap_size_of_children() + + self.peers.heap_size_of_children() + self.header_ids.heap_size_of_children(), } } @@ -635,16 +635,7 @@ impl ChainSync { match self.last_imported_block { None => 0, Some(x) => x } } - /// Find some headers or blocks to download for a peer. - fn request_blocks(&mut self, io: &mut SyncIo, peer_id: PeerId, ignore_others: bool) { - self.clear_peer_download(peer_id); - - if io.chain().queue_info().is_full() { - self.pause_sync(); - return; - } - - // check to see if we need to download any block bodies first + fn find_block_bodies_hashes_to_request(&self, ignore_others: bool) -> (Vec, Vec) { let mut needed_bodies: Vec = Vec::new(); let mut needed_numbers: Vec = Vec::new(); @@ -664,74 +655,88 @@ impl ChainSync { } } } + (needed_bodies, needed_numbers) + } + + /// Find some headers or blocks to download for a peer. + fn request_blocks(&mut self, io: &mut SyncIo, peer_id: PeerId, ignore_others: bool) { + self.clear_peer_download(peer_id); + + if io.chain().queue_info().is_full() { + self.pause_sync(); + return; + } + + // check to see if we need to download any block bodies first + let (needed_bodies, needed_numbers) = self.find_block_bodies_hashes_to_request(ignore_others); if !needed_bodies.is_empty() { let (head, _) = self.headers.range_iter().next().unwrap(); if needed_numbers.first().unwrap() - head > self.max_download_ahead_blocks as BlockNumber { trace!(target: "sync", "{}: Stalled download ({} vs {}), helping with downloading block bodies", peer_id, needed_numbers.first().unwrap(), head); self.request_blocks(io, peer_id, true); - return; + } else { + self.downloading_bodies.extend(needed_numbers.iter()); + replace(&mut self.peers.get_mut(&peer_id).unwrap().asking_blocks, needed_numbers); + self.request_bodies(io, peer_id, needed_bodies); + } + return; + } + + // check if need to download headers + let mut start = 0; + if !self.have_common_block { + // download backwards until common block is found 1 header at a time + let chain_info = io.chain().chain_info(); + start = chain_info.best_block_number; + if !self.headers.is_empty() { + start = min(start, self.headers.range_iter().next().unwrap().0 - 1); + } + if start == 0 { + self.have_common_block = true; //reached genesis + self.last_imported_hash = Some(chain_info.genesis_hash); + self.last_imported_block = Some(0); + } + } + if self.have_common_block { + let mut headers: Vec = Vec::new(); + let mut prev = self.current_base_block() + 1; + let head = self.headers.range_iter().next().map(|(h, _)| h); + for (next, ref items) in self.headers.range_iter() { + if !headers.is_empty() { + break; + } + if next <= prev { + prev = next + items.len() as BlockNumber; + continue; + } + let mut block = prev; + while block < next && headers.len() < MAX_HEADERS_TO_REQUEST { + if ignore_others || !self.downloading_headers.contains(&(block as BlockNumber)) { + headers.push(block as BlockNumber); + } + block += 1; + } + prev = next + items.len() as BlockNumber; + } + + if !headers.is_empty() { + start = headers[0]; + if head.is_some() && start > head.unwrap() && start - head.unwrap() > self.max_download_ahead_blocks as BlockNumber { + trace!(target: "sync", "{}: Stalled download ({} vs {}), helping with downloading headers", peer_id, start, head.unwrap()); + self.request_blocks(io, peer_id, true); + return; + } + let count = headers.len(); + self.downloading_headers.extend(headers.iter()); + replace(&mut self.peers.get_mut(&peer_id).unwrap().asking_blocks, headers); + assert!(!self.headers.have_item(&start)); + self.request_headers_by_number(io, peer_id, start, count, 0, false); } - self.downloading_bodies.extend(needed_numbers.iter()); - replace(&mut self.peers.get_mut(&peer_id).unwrap().asking_blocks, needed_numbers); - self.request_bodies(io, peer_id, needed_bodies); } else { - // check if need to download headers - let mut start = 0; - if !self.have_common_block { - // download backwards until common block is found 1 header at a time - let chain_info = io.chain().chain_info(); - start = chain_info.best_block_number; - if !self.headers.is_empty() { - start = min(start, self.headers.range_iter().next().unwrap().0 - 1); - } - if start == 0 { - self.have_common_block = true; //reached genesis - self.last_imported_hash = Some(chain_info.genesis_hash); - self.last_imported_block = Some(0); - } - } - if self.have_common_block { - let mut headers: Vec = Vec::new(); - let mut prev = self.current_base_block() + 1; - let head = self.headers.range_iter().next().map(|(h, _)| h); - for (next, ref items) in self.headers.range_iter() { - if !headers.is_empty() { - break; - } - if next <= prev { - prev = next + items.len() as BlockNumber; - continue; - } - let mut block = prev; - while block < next && headers.len() < MAX_HEADERS_TO_REQUEST { - if ignore_others || !self.downloading_headers.contains(&(block as BlockNumber)) { - headers.push(block as BlockNumber); - } - block += 1; - } - prev = next + items.len() as BlockNumber; - } - - if !headers.is_empty() { - start = headers[0]; - if head.is_some() && start > head.unwrap() && start - head.unwrap() > self.max_download_ahead_blocks as BlockNumber { - trace!(target: "sync", "{}: Stalled download ({} vs {}), helping with downloading headers", peer_id, start, head.unwrap()); - self.request_blocks(io, peer_id, true); - return; - } - let count = headers.len(); - self.downloading_headers.extend(headers.iter()); - replace(&mut self.peers.get_mut(&peer_id).unwrap().asking_blocks, headers); - assert!(!self.headers.have_item(&start)); - self.request_headers_by_number(io, peer_id, start, count, 0, false); - } - } - else { - // continue search for common block - self.downloading_headers.insert(start); - self.request_headers_by_number(io, peer_id, start, 1, 0, false); - } + // continue search for common block + self.downloading_headers.insert(start); + self.request_headers_by_number(io, peer_id, start, 1, 0, false); } } From 5f97d519673d2ad05d4739a409728ac10f271393 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 2 Mar 2016 05:46:38 +0100 Subject: [PATCH 06/14] renamed poll_indexed to poll_manager --- rpc/src/v1/helpers/mod.rs | 4 ++-- .../{poll_indexer.rs => poll_manager.rs} | 22 +++++++++---------- rpc/src/v1/impls/eth.rs | 8 +++---- 3 files changed, 17 insertions(+), 17 deletions(-) rename rpc/src/v1/helpers/{poll_indexer.rs => poll_manager.rs} (88%) diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index d212d74ab..b1a5c05ba 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -mod poll_indexer; +mod poll_manager; mod poll_filter; -pub use self::poll_indexer::PollIndexer; +pub use self::poll_manager::PollManager; pub use self::poll_filter::PollFilter; diff --git a/rpc/src/v1/helpers/poll_indexer.rs b/rpc/src/v1/helpers/poll_manager.rs similarity index 88% rename from rpc/src/v1/helpers/poll_indexer.rs rename to rpc/src/v1/helpers/poll_manager.rs index 11cb30935..1e7addb25 100644 --- a/rpc/src/v1/helpers/poll_indexer.rs +++ b/rpc/src/v1/helpers/poll_manager.rs @@ -39,30 +39,30 @@ impl Clone for PollInfo where F: Clone { } /// Indexes all poll requests. -/// +/// /// Lazily garbage collects unused polls info. -pub struct PollIndexer where T: Timer { +pub struct PollManager where T: Timer { polls: TransientHashMap, T>, next_available_id: PollId } -impl PollIndexer { +impl PollManager { /// Creates new instance of indexer. pub fn new() -> Self { - PollIndexer::new_with_timer(Default::default()) + PollManager::new_with_timer(Default::default()) } } -impl PollIndexer where T: Timer { +impl PollManager where T: Timer { pub fn new_with_timer(timer: T) -> Self { - PollIndexer { + PollManager { polls: TransientHashMap::new_with_timer(POLL_LIFETIME, timer), next_available_id: 0 } } - /// Returns id which can be used for new poll. - /// + /// Returns id which can be used for new poll. + /// /// Stores information when last poll happend. pub fn create_poll(&mut self, filter: F, block: BlockNumber) -> PollId { self.polls.prune(); @@ -84,7 +84,7 @@ impl PollIndexer where T: Timer { } /// Returns number of block when last poll happend. - pub fn get_poll(&mut self, id: &PollId) -> Option<&PollInfo> { + pub fn get_poll_info(&mut self, id: &PollId) -> Option<&PollInfo> { self.polls.prune(); self.polls.get(id) } @@ -99,7 +99,7 @@ impl PollIndexer where T: Timer { mod tests { use std::cell::RefCell; use transient_hashmap::Timer; - use v1::helpers::PollIndexer; + use v1::helpers::PollManager; struct TestTimer<'a> { time: &'a RefCell @@ -118,7 +118,7 @@ mod tests { time: &time }; - let mut indexer = PollIndexer::new_with_timer(timer); + let mut indexer = PollManager::new_with_timer(timer); assert_eq!(indexer.create_poll(false, 20), 0); assert_eq!(indexer.create_poll(true, 20), 1); diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 32c143a84..880c45041 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -25,7 +25,7 @@ use ethcore::views::*; use ethcore::ethereum::denominations::shannon; use v1::traits::{Eth, EthFilter}; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, OptionalValue, Index, Filter, Log}; -use v1::helpers::{PollFilter, PollIndexer}; +use v1::helpers::{PollFilter, PollManager}; /// Eth rpc implementation. pub struct EthClient { @@ -214,7 +214,7 @@ impl Eth for EthClient { /// Eth filter rpc implementation. pub struct EthFilterClient { client: Weak, - polls: Mutex>, + polls: Mutex>, } impl EthFilterClient { @@ -222,7 +222,7 @@ impl EthFilterClient { pub fn new(client: &Arc) -> Self { EthFilterClient { client: Arc::downgrade(client), - polls: Mutex::new(PollIndexer::new()) + polls: Mutex::new(PollManager::new()) } } } @@ -263,7 +263,7 @@ impl EthFilter for EthFilterClient { let client = take_weak!(self.client); from_params::<(Index,)>(params) .and_then(|(index,)| { - let info = self.polls.lock().unwrap().get_poll(&index.value()).cloned(); + let info = self.polls.lock().unwrap().get_poll_info(&index.value()).cloned(); match info { None => Ok(Value::Array(vec![] as Vec)), Some(info) => match info.filter { From 05897ddbb0fd97e69209ef44b72886a5979a7e4c Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 2 Mar 2016 05:50:28 +0100 Subject: [PATCH 07/14] fixed pending transaction mock implementation --- rpc/src/v1/impls/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 880c45041..f885441c5 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -280,7 +280,7 @@ impl EthFilter for EthFilterClient { }, PollFilter::PendingTransaction => { // TODO: fix implementation - to_value(&client.chain_info().best_block_hash).map(|v| Value::Array(vec![v])) + to_value(&vec![H256::default()]) }, PollFilter::Logs(mut filter) => { filter.from_block = BlockId::Number(info.block_number); From 28c5f6f9c3acdab23a8b990cad133da6d9381bb6 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 2 Mar 2016 12:32:30 +0100 Subject: [PATCH 08/14] fixed failing test --- rpc/src/v1/helpers/poll_manager.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rpc/src/v1/helpers/poll_manager.rs b/rpc/src/v1/helpers/poll_manager.rs index 1e7addb25..33b98af45 100644 --- a/rpc/src/v1/helpers/poll_manager.rs +++ b/rpc/src/v1/helpers/poll_manager.rs @@ -124,21 +124,21 @@ mod tests { *time.borrow_mut() = 10; indexer.update_poll(&0, 21); - assert_eq!(indexer.get_poll(&0).unwrap().filter, false); - assert_eq!(indexer.get_poll(&0).unwrap().block_number, 21); + assert_eq!(indexer.get_poll_info(&0).unwrap().filter, false); + assert_eq!(indexer.get_poll_info(&0).unwrap().block_number, 21); *time.borrow_mut() = 30; indexer.update_poll(&1, 23); - assert_eq!(indexer.get_poll(&1).unwrap().filter, true); - assert_eq!(indexer.get_poll(&1).unwrap().block_number, 23); + assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true); + assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23); *time.borrow_mut() = 75; indexer.update_poll(&0, 30); - assert!(indexer.get_poll(&0).is_none()); - assert_eq!(indexer.get_poll(&1).unwrap().filter, true); - assert_eq!(indexer.get_poll(&1).unwrap().block_number, 23); + assert!(indexer.get_poll_info(&0).is_none()); + assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true); + assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23); indexer.remove_poll(&1); - assert!(indexer.get_poll(&1).is_none()); + assert!(indexer.get_poll_info(&1).is_none()); } } From bdf3b197ad8400dc1b9037b49cd695b971ee578b Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 2 Mar 2016 12:42:08 +0100 Subject: [PATCH 09/14] limit serde codegen only to rpc types submodule --- rpc/build.rs | 4 ++-- rpc/src/lib.rs | 33 +++++++++++++++++++++++++++++---- rpc/src/lib.rs.in | 30 ------------------------------ rpc/src/v1/impls/web3.rs | 4 +--- rpc/src/v1/types/mod.rs | 22 ++++------------------ rpc/src/v1/types/mod.rs.in | 35 +++++++++++++++++++++++++++++++++++ 6 files changed, 71 insertions(+), 57 deletions(-) delete mode 100644 rpc/src/lib.rs.in create mode 100644 rpc/src/v1/types/mod.rs.in diff --git a/rpc/build.rs b/rpc/build.rs index fe1a55694..b5adeaba1 100644 --- a/rpc/build.rs +++ b/rpc/build.rs @@ -9,8 +9,8 @@ mod inner { pub fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); - let src = Path::new("src/lib.rs.in"); - let dst = Path::new(&out_dir).join("lib.rs"); + let src = Path::new("src/v1/types/mod.rs.in"); + let dst = Path::new(&out_dir).join("mod.rs"); let mut registry = syntex::Registry::new(); diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 8dd58d0b8..4559f766c 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -28,8 +28,33 @@ extern crate ethcore_util as util; extern crate ethcore; extern crate ethsync; -#[cfg(feature = "serde_macros")] -include!("lib.rs.in"); +use self::jsonrpc_core::{IoHandler, IoDelegate}; -#[cfg(not(feature = "serde_macros"))] -include!(concat!(env!("OUT_DIR"), "/lib.rs")); +pub mod v1; + +/// Http server. +pub struct HttpServer { + handler: IoHandler, + threads: usize +} + +impl HttpServer { + /// Construct new http server object with given number of threads. + pub fn new(threads: usize) -> HttpServer { + HttpServer { + handler: IoHandler::new(), + threads: threads + } + } + + /// Add io delegate. + pub fn add_delegate(&mut self, delegate: IoDelegate) where D: Send + Sync + 'static { + self.handler.add_delegate(delegate); + } + + /// Start server asynchronously in new thread + pub fn start_async(self, addr: &str, cors_domain: &str) { + let server = jsonrpc_http_server::Server::new(self.handler, self.threads); + server.start_async(addr, jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain.to_owned())) + } +} diff --git a/rpc/src/lib.rs.in b/rpc/src/lib.rs.in deleted file mode 100644 index e17f2b3bb..000000000 --- a/rpc/src/lib.rs.in +++ /dev/null @@ -1,30 +0,0 @@ -use self::jsonrpc_core::{IoHandler, IoDelegate}; - -pub mod v1; - -/// Http server. -pub struct HttpServer { - handler: IoHandler, - threads: usize -} - -impl HttpServer { - /// Construct new http server object with given number of threads. - pub fn new(threads: usize) -> HttpServer { - HttpServer { - handler: IoHandler::new(), - threads: threads - } - } - - /// Add io delegate. - pub fn add_delegate(&mut self, delegate: IoDelegate) where D: Send + Sync + 'static { - self.handler.add_delegate(delegate); - } - - /// Start server asynchronously in new thread - pub fn start_async(self, addr: &str, cors_domain: &str) { - let server = jsonrpc_http_server::Server::new(self.handler, self.threads); - server.start_async(addr, jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain.to_owned())) - } -} diff --git a/rpc/src/v1/impls/web3.rs b/rpc/src/v1/impls/web3.rs index 0a237d56b..1cfe35631 100644 --- a/rpc/src/v1/impls/web3.rs +++ b/rpc/src/v1/impls/web3.rs @@ -30,9 +30,7 @@ impl Web3Client { impl Web3 for Web3Client { fn client_version(&self, params: Params) -> Result { match params { - Params::None => { - Ok(Value::String(version())), - } + Params::None => Ok(Value::String(version())), _ => Err(Error::invalid_params()) } } diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index 34c1f1cff..adf9be071 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -14,22 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -mod block; -mod block_number; -mod bytes; -mod filter; -mod index; -mod log; -mod optionals; -mod sync; -mod transaction; +#[cfg(feature = "serde_macros")] +include!("mod.rs.in"); -pub use self::block::{Block, BlockTransactions}; -pub use self::block_number::BlockNumber; -pub use self::bytes::Bytes; -pub use self::filter::Filter; -pub use self::index::Index; -pub use self::log::Log; -pub use self::optionals::OptionalValue; -pub use self::sync::{SyncStatus, SyncInfo}; -pub use self::transaction::Transaction; +#[cfg(not(feature = "serde_macros"))] +include!(concat!(env!("OUT_DIR"), "/mod.rs")); diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in new file mode 100644 index 000000000..34c1f1cff --- /dev/null +++ b/rpc/src/v1/types/mod.rs.in @@ -0,0 +1,35 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +mod block; +mod block_number; +mod bytes; +mod filter; +mod index; +mod log; +mod optionals; +mod sync; +mod transaction; + +pub use self::block::{Block, BlockTransactions}; +pub use self::block_number::BlockNumber; +pub use self::bytes::Bytes; +pub use self::filter::Filter; +pub use self::index::Index; +pub use self::log::Log; +pub use self::optionals::OptionalValue; +pub use self::sync::{SyncStatus, SyncInfo}; +pub use self::transaction::Transaction; From 190468e1f85175b72f1f460dd5810e7ae4a62da6 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 2 Mar 2016 14:03:43 +0100 Subject: [PATCH 10/14] fixed trailing , --- rpc/src/v1/helpers/poll_manager.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rpc/src/v1/helpers/poll_manager.rs b/rpc/src/v1/helpers/poll_manager.rs index 33b98af45..36a6352c2 100644 --- a/rpc/src/v1/helpers/poll_manager.rs +++ b/rpc/src/v1/helpers/poll_manager.rs @@ -57,7 +57,7 @@ impl PollManager where T: Timer { pub fn new_with_timer(timer: T) -> Self { PollManager { polls: TransientHashMap::new_with_timer(POLL_LIFETIME, timer), - next_available_id: 0 + next_available_id: 0, } } @@ -70,7 +70,7 @@ impl PollManager where T: Timer { self.next_available_id += 1; self.polls.insert(id, PollInfo { filter: filter, - block_number: block + block_number: block, }); id } @@ -102,7 +102,7 @@ mod tests { use v1::helpers::PollManager; struct TestTimer<'a> { - time: &'a RefCell + time: &'a RefCell, } impl<'a> Timer for TestTimer<'a> { @@ -115,7 +115,7 @@ mod tests { fn test_poll_indexer() { let time = RefCell::new(0); let timer = TestTimer { - time: &time + time: &time, }; let mut indexer = PollManager::new_with_timer(timer); From b1a62575c9fe86ebe8299c8f6238892fedfeb6c0 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 2 Mar 2016 14:09:44 +0100 Subject: [PATCH 11/14] fixed PollFilter::PendingTransaction returning empty hash --- rpc/src/v1/impls/eth.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 2baaf5225..2313d5114 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -323,8 +323,8 @@ impl EthFilter for EthFilterClient { to_value(&hashes) }, PollFilter::PendingTransaction => { - // TODO: fix implementation - to_value(&vec![H256::default()]) + // TODO: fix implementation once TransactionQueue is merged + to_value(&vec![] as &Vec) }, PollFilter::Logs(mut filter) => { filter.from_block = BlockId::Number(info.block_number); From 02aad03f9204acfb51ec65b52c2d35395ee76197 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Wed, 2 Mar 2016 17:06:53 +0300 Subject: [PATCH 12/14] helpers --- ethcore/src/tests/client.rs | 19 +++++++++++++ ethcore/src/tests/helpers.rs | 52 +++++++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 83df81fa2..e2671d63e 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -106,3 +106,22 @@ fn can_collect_garbage() { client.tick(); assert!(client.blockchain_cache_info().blocks < 100 * 1024); } + +#[test] +fn can_handle_long_fork() { + let client_result = generate_dummy_client(1200); + let client = client_result.reference(); + for _ in 0..10 { + client.import_verified_blocks(&IoChannel::disconnected()); + } + assert_eq!(1200, client.chain_info().best_block_number); + + push_blocks_to_client(client, 45, 1201, 800); + push_blocks_to_client(client, 49, 1201, 800); + push_blocks_to_client(client, 53, 1201, 600); + + for _ in 0..20 { + client.import_verified_blocks(&IoChannel::disconnected()); + } + assert_eq!(2000, client.chain_info().best_block_number); +} diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 44ad667b9..bb9a44614 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -156,10 +156,9 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult rolling_block_number = rolling_block_number + 1; rolling_timestamp = rolling_timestamp + 10; - if let Err(_) = client.import_block(create_test_block(&header)) { - panic!("error importing block which is valid by definition"); + if let Err(e) = client.import_block(create_test_block(&header)) { + panic!("error importing block which is valid by definition: {:?}", e); } - } client.flush_queue(); client.import_verified_blocks(&IoChannel::disconnected()); @@ -170,6 +169,34 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult } } +pub fn push_blocks_to_client(client: &Arc, timestamp_salt: u64, starting_number: usize, block_number: usize) { + let test_spec = get_test_spec(); + let test_engine = test_spec.to_engine().unwrap(); + let state_root = test_engine.spec().genesis_header().state_root; + let mut rolling_hash = client.chain_info().best_block_hash; + let mut rolling_block_number = starting_number as u64; + let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10; + + for _ in 0..block_number { + let mut header = Header::new(); + + header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap()); + header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap()); + header.timestamp = rolling_timestamp; + header.number = rolling_block_number; + header.parent_hash = rolling_hash; + header.state_root = state_root.clone(); + + rolling_hash = header.hash(); + rolling_block_number = rolling_block_number + 1; + rolling_timestamp = rolling_timestamp + 10; + + if let Err(e) = client.import_block(create_test_block(&header)) { + panic!("error importing block which is valid by definition: {:?}", e); + } + } +} + pub fn get_test_client_with_blocks(blocks: Vec) -> GuardedTempResult> { let dir = RandomTempPath::new(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); @@ -253,18 +280,29 @@ pub fn get_temp_state_in(path: &Path) -> State { pub fn get_good_dummy_block_seq(count: usize) -> Vec { let test_spec = get_test_spec(); let test_engine = test_spec.to_engine().unwrap(); - let mut parent = test_engine.spec().genesis_header().hash(); + get_good_dummy_block_fork_seq(1, count, &test_engine.spec().genesis_header().hash()) +} + +pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_hash: &H256) -> Vec { + let test_spec = get_test_spec(); + let test_engine = test_spec.to_engine().unwrap(); + let mut rolling_timestamp = start_number as u64 * 10; + let mut parent = *parent_hash; let mut r = Vec::new(); - for i in 1 .. count + 1 { + for i in start_number .. start_number + count + 1 { let mut block_header = Header::new(); block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap()); - block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap()); - block_header.timestamp = i as u64; + block_header.difficulty = U256::from(i).mul(U256([0, 1, 0, 0])); + block_header.timestamp = rolling_timestamp; block_header.number = i as u64; block_header.parent_hash = parent; block_header.state_root = test_engine.spec().genesis_header().state_root; + parent = block_header.hash(); + rolling_timestamp = rolling_timestamp + 10; + r.push(create_test_block(&block_header)); + } r } From dadc2a96eafac3a72bef19d84f40357adbcf6032 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 3 Mar 2016 12:39:19 +0100 Subject: [PATCH 13/14] shrink_to_fit after removing hashes. --- ethcore/src/blockchain/blockchain.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 185bcaad3..eac6da6a2 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -759,6 +759,14 @@ impl BlockChain { // TODO: handle block_hashes properly. block_hashes.clear(); + + blocks.shrink_to_fit(); + block_details.shrink_to_fit(); + block_hashes.shrink_to_fit(); + transaction_addresses.shrink_to_fit(); + block_logs.shrink_to_fit(); + blocks_blooms.shrink_to_fit(); + block_receipts.shrink_to_fit(); } if self.cache_size().total() < self.max_cache_size { break; } } From f8dc1f2e3aa96283de3e16d3bbe1e2961b916836 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 3 Mar 2016 12:56:34 +0100 Subject: [PATCH 14/14] Avoid leaking block_details. Fixes #576 --- ethcore/src/blockchain/blockchain.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index eac6da6a2..f412a8240 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -448,7 +448,8 @@ impl BlockChain { let mut write_details = self.block_details.write().unwrap(); for (hash, details) in update.block_details.into_iter() { batch.put_extras(&hash, &details); - write_details.insert(hash, details); + write_details.insert(hash.clone(), details); + self.note_used(CacheID::Extras(ExtrasIndex::BlockDetails, hash)); } let mut write_receipts = self.block_receipts.write().unwrap();