diff --git a/.travis.yml b/.travis.yml
index 8d2349dae..7213b8f09 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,6 +13,8 @@ matrix:
allow_failures:
- rust: nightly
include:
+ - rust: stable
+ env: FEATURES="--features travis-beta" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
- rust: beta
env: FEATURES="--features travis-beta" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
- rust: nightly
@@ -52,7 +54,7 @@ after_success: |
./kcov-master/tmp/usr/local/bin/kcov --coveralls-id=${TRAVIS_JOB_ID} --exclude-pattern /usr/,/.cargo,/root/.multirust target/kcov target/debug/parity-* &&
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
- [ $TRAVIS_RUST_VERSION = beta ] &&
+ [ $TRAVIS_RUST_VERSION = stable ] &&
cargo doc --no-deps --verbose ${KCOV_FEATURES} ${TARGETS} &&
echo '' > target/doc/index.html &&
pip install --user ghp-import &&
diff --git a/Cargo.lock b/Cargo.lock
index bd59e41fe..510e69b59 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -81,6 +81,15 @@ name = "cfg-if"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "chrono"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "clippy"
version = "0.0.44"
@@ -235,6 +244,7 @@ dependencies = [
"serde_codegen 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.7.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 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -243,6 +253,7 @@ version = "0.9.99"
dependencies = [
"arrayvec 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"bigint 0.1.0",
+ "chrono 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -841,6 +852,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 = "registry+https://github.com/rust-lang/crates.io-index"
+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/block.rs b/ethcore/src/block.rs
index f5788baba..68f647e37 100644
--- a/ethcore/src/block.rs
+++ b/ethcore/src/block.rs
@@ -220,8 +220,8 @@ impl<'x> OpenBlock<'x> {
/// NOTE Will check chain constraints and the uncle number but will NOT check
/// that the header itself is actually valid.
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
- if self.block.base.uncles.len() >= self.engine.maximum_uncle_count() {
- return Err(BlockError::TooManyUncles(OutOfBounds{min: None, max: Some(self.engine.maximum_uncle_count()), found: self.block.base.uncles.len()}));
+ if self.block.base.uncles.len() + 1 > self.engine.maximum_uncle_count() {
+ return Err(BlockError::TooManyUncles(OutOfBounds{min: None, max: Some(self.engine.maximum_uncle_count()), found: self.block.base.uncles.len() + 1}));
}
// TODO: check number
// TODO: check not a direct ancestor (use last_hashes for that)
diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs
index 185bcaad3..e529f50af 100644
--- a/ethcore/src/blockchain/blockchain.rs
+++ b/ethcore/src/blockchain/blockchain.rs
@@ -78,7 +78,7 @@ pub trait BlockProvider {
}
/// Get a list of uncles for a given block.
- /// Returns None if block deos not exist.
+ /// Returns None if block does not exist.
fn uncles(&self, hash: &H256) -> Option> {
self.block(hash).map(|bytes| BlockView::new(&bytes).uncles())
}
@@ -227,6 +227,24 @@ impl BlockProvider for BlockChain {
const COLLECTION_QUEUE_SIZE: usize = 8;
+pub struct AncestryIter<'a> {
+ current: H256,
+ chain: &'a BlockChain,
+}
+
+impl<'a> Iterator for AncestryIter<'a> {
+ type Item = H256;
+ fn next(&mut self) -> Option {
+ if self.current.is_zero() {
+ Option::None
+ } else {
+ let mut n = self.chain.block_details(&self.current).unwrap().parent;
+ mem::swap(&mut self.current, &mut n);
+ Some(n)
+ }
+ }
+}
+
impl BlockChain {
/// Create new instance of blockchain from given Genesis
pub fn new(config: BlockChainConfig, genesis: &[u8], path: &Path) -> BlockChain {
@@ -448,7 +466,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();
@@ -473,10 +492,35 @@ impl BlockChain {
self.extras_db.write(batch).unwrap();
}
- /// Given a block's `parent`, find every block header which represents a valid uncle.
- pub fn find_uncle_headers(&self, _parent: &H256) -> Vec {
- // TODO
- Vec::new()
+ /// Iterator that lists `first` and then all of `first`'s ancestors, by hash.
+ pub fn ancestry_iter(&self, first: H256) -> Option {
+ if self.is_known(&first) {
+ Some(AncestryIter {
+ current: first,
+ chain: &self,
+ })
+ } else {
+ None
+ }
+ }
+
+ /// Given a block's `parent`, find every block header which represents a valid possible uncle.
+ pub fn find_uncle_headers(&self, parent: &H256, uncle_generations: usize) -> Option> {
+ if !self.is_known(parent) { return None; }
+
+ let mut excluded = HashSet::new();
+ for a in self.ancestry_iter(parent.clone()).unwrap().take(uncle_generations) {
+ excluded.extend(self.uncle_hashes(&a).unwrap().into_iter());
+ excluded.insert(a);
+ }
+
+ let mut ret = Vec::new();
+ for a in self.ancestry_iter(parent.clone()).unwrap().skip(1).take(uncle_generations) {
+ ret.extend(self.block_details(&a).unwrap().children.iter()
+ .filter_map(|h| if excluded.contains(h) { None } else { self.block_header(h) })
+ );
+ }
+ Some(ret)
}
/// Get inserted block info which is critical to preapre extras updates.
@@ -759,6 +803,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; }
}
@@ -809,6 +861,66 @@ mod tests {
assert_eq!(bc.block_hash(2), None);
}
+ #[test]
+ fn check_ancestry_iter() {
+ let mut canon_chain = ChainGenerator::default();
+ let mut finalizer = BlockFinalizer::default();
+ let genesis = canon_chain.generate(&mut finalizer).unwrap();
+ let genesis_hash = BlockView::new(&genesis).header_view().sha3();
+
+ let temp = RandomTempPath::new();
+ let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
+
+ let mut block_hashes = vec![genesis_hash.clone()];
+ for _ in 0..10 {
+ let block = canon_chain.generate(&mut finalizer).unwrap();
+ block_hashes.push(BlockView::new(&block).header_view().sha3());
+ bc.insert_block(&block, vec![]);
+ }
+
+ block_hashes.reverse();
+
+ assert_eq!(bc.ancestry_iter(block_hashes[0].clone()).unwrap().collect::>(), block_hashes)
+ }
+
+ #[test]
+ #[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
+ fn test_find_uncles() {
+ let mut canon_chain = ChainGenerator::default();
+ let mut finalizer = BlockFinalizer::default();
+ let genesis = canon_chain.generate(&mut finalizer).unwrap();
+ let b1b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
+ let b1a = canon_chain.generate(&mut finalizer).unwrap();
+ let b2b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
+ let b2a = canon_chain.generate(&mut finalizer).unwrap();
+ let b3b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
+ let b3a = canon_chain.generate(&mut finalizer).unwrap();
+ let b4b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
+ let b4a = canon_chain.generate(&mut finalizer).unwrap();
+ let b5b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
+ let b5a = canon_chain.generate(&mut finalizer).unwrap();
+
+ let temp = RandomTempPath::new();
+ let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
+ bc.insert_block(&b1a, vec![]);
+ bc.insert_block(&b1b, vec![]);
+ bc.insert_block(&b2a, vec![]);
+ bc.insert_block(&b2b, vec![]);
+ bc.insert_block(&b3a, vec![]);
+ bc.insert_block(&b3b, vec![]);
+ bc.insert_block(&b4a, vec![]);
+ bc.insert_block(&b4b, vec![]);
+ bc.insert_block(&b5a, vec![]);
+ bc.insert_block(&b5b, vec![]);
+
+ assert_eq!(
+ [&b4b, &b3b, &b2b].iter().map(|b| BlockView::new(b).header()).collect::>(),
+ bc.find_uncle_headers(&BlockView::new(&b4a).header_view().sha3(), 3).unwrap()
+ );
+
+ // TODO: insert block that already includes one of them as an uncle to check it's not allowed.
+ }
+
#[test]
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn test_small_fork() {
diff --git a/ethcore/src/client.rs b/ethcore/src/client.rs
index fdcd6c057..852ba6a36 100644
--- a/ethcore/src/client.rs
+++ b/ethcore/src/client.rs
@@ -16,6 +16,8 @@
//! Blockchain database client.
+use std::marker::PhantomData;
+use std::sync::atomic::AtomicBool;
use util::*;
use util::panics::*;
use blockchain::{BlockChain, BlockProvider};
@@ -35,6 +37,7 @@ use transaction::LocalizedTransaction;
use extras::TransactionAddress;
use filter::Filter;
use log_entry::LocalizedLogEntry;
+use util::keys::store::SecretStore;
pub use block_queue::{BlockQueueConfig, BlockQueueInfo};
pub use blockchain::{TreeRoute, BlockChainConfig, CacheSize as BlockChainCacheSize};
@@ -76,12 +79,24 @@ pub enum BlockStatus {
}
/// Client configuration. Includes configs for all sub-systems.
-#[derive(Debug, Default)]
+#[derive(Debug)]
pub struct ClientConfig {
/// Block queue configuration.
pub queue: BlockQueueConfig,
/// Blockchain configuration.
pub blockchain: BlockChainConfig,
+ /// Prefer journal rather than archive.
+ pub prefer_journal: bool,
+}
+
+impl Default for ClientConfig {
+ fn default() -> ClientConfig {
+ ClientConfig {
+ queue: Default::default(),
+ blockchain: Default::default(),
+ prefer_journal: false,
+ }
+ }
}
/// Information about the blockchain gathered together.
@@ -126,6 +141,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get address nonce.
fn nonce(&self, address: &Address) -> U256;
+ /// Get block hash.
+ fn block_hash(&self, id: BlockId) -> Option;
+
/// Get address code.
fn code(&self, address: &Address) -> Option;
@@ -188,7 +206,7 @@ impl ClientReport {
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
-pub struct Client {
+pub struct Client where V: Verifier {
chain: Arc>,
engine: Arc>,
state_db: Mutex,
@@ -198,21 +216,31 @@ pub struct Client {
panic_handler: Arc,
// for sealing...
+ sealing_enabled: AtomicBool,
sealing_block: Mutex