diff --git a/Cargo.lock b/Cargo.lock index 76254eca6..1467edd23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -285,6 +285,11 @@ name = "dtoa" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "either" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "elastic-array" version = "0.6.0" @@ -515,6 +520,7 @@ dependencies = [ "ethcore-network 1.6.0", "ethcore-util 1.6.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", @@ -943,6 +949,14 @@ name = "itertools" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "itertools" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itoa" version = "0.1.1" @@ -2424,6 +2438,7 @@ dependencies = [ "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf" "checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76" "checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d" +"checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91" "checksum elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)" = "" "checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5" "checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "" @@ -2445,6 +2460,7 @@ dependencies = [ "checksum igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8c12b1795b8b168f577c45fa10379b3814dcb11b7ab702406001f0d63f40484" "checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c" "checksum itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "086e1fa5fe48840b1cfdef3a20c7e3115599f8d5c4c87ef32a794a7cdd184d76" +"checksum itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d95557e7ba6b71377b0f2c3b3ae96c53f1b75a926a6901a500f557a370af730a" "checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" "checksum jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum jsonrpc-http-server 7.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 534b0e5c1..d2444dd59 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -22,6 +22,7 @@ time = "0.1" smallvec = "0.3.1" futures = "0.1" rand = "0.3" +itertools = "0.5" [features] default = [] diff --git a/ethcore/light/src/cht.rs b/ethcore/light/src/cht.rs index 1c7d11b48..2f6659353 100644 --- a/ethcore/light/src/cht.rs +++ b/ethcore/light/src/cht.rs @@ -34,7 +34,7 @@ macro_rules! val { ($hash: expr, $td: expr) => {{ let mut stream = RlpStream::new_list(2); stream.append(&$hash).append(&$td); - stream.out() + stream.drain() }} } @@ -116,6 +116,26 @@ pub fn build(cht_num: u64, mut fetcher: F) -> Option> }) } +/// Compute a CHT root from an iterator of (hash, td) pairs. Fails if shorter than +/// SIZE items. The items are assumed to proceed sequentially from `start_number(cht_num)`. +/// Discards the trie's nodes. +pub fn compute_root(cht_num: u64, iterable: I) -> Option + where I: IntoIterator +{ + let mut v = Vec::with_capacity(SIZE as usize); + let start_num = start_number(cht_num) as usize; + + for (i, (h, td)) in iterable.into_iter().take(SIZE as usize).enumerate() { + v.push((key!(i + start_num).to_vec(), val!(h, td).to_vec())) + } + + if v.len() == SIZE as usize { + Some(::util::triehash::trie_root(v)) + } else { + None + } +} + /// Convert a block number to a CHT number. /// Returns `None` for `block_num` == 0, `Some` otherwise. pub fn block_to_cht_number(block_num: u64) -> Option { diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index 531eaf350..bc007785e 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -173,26 +173,34 @@ impl HeaderChain { // produce next CHT root if it's time. let earliest_era = *candidates.keys().next().expect("at least one era just created; qed"); if earliest_era + HISTORY + cht::SIZE <= number { - let mut values = Vec::with_capacity(cht::SIZE as usize); - { - let mut headers = self.headers.write(); - for i in (0..cht::SIZE).map(|x| x + earliest_era) { + let cht_num = cht::block_to_cht_number(earliest_era) + .expect("fails only for number == 0; genesis never imported; qed"); + debug_assert_eq!(cht_num as usize, self.cht_roots.lock().len()); + + let mut headers = self.headers.write(); + + let cht_root = { + let mut i = earliest_era; + + // iterable function which removes the candidates as it goes + // along. this will only be called until the CHT is complete. + let iter = || { let era_entry = candidates.remove(&i) .expect("all eras are sequential with no gaps; qed"); + i += 1; for ancient in &era_entry.candidates { headers.remove(&ancient.hash); } - values.push(( - ::rlp::encode(&i).to_vec(), - ::rlp::encode(&era_entry.canonical_hash).to_vec(), - )); - } - } + let canon = &era_entry.candidates[0]; + (canon.hash, canon.total_difficulty) + }; + cht::compute_root(cht_num, ::itertools::repeat_call(iter)) + .expect("fails only when too few items; this is checked; qed") + }; - let cht_root = ::util::triehash::trie_root(values); - debug!(target: "chain", "Produced CHT {} root: {:?}", (earliest_era - 1) % cht::SIZE, cht_root); + debug!(target: "chain", "Produced CHT {} root: {:?}", cht_num, cht_root); self.cht_roots.lock().push(cht_root); } diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index 6f57e075f..6236ba118 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -68,6 +68,7 @@ extern crate smallvec; extern crate time; extern crate futures; extern crate rand; +extern crate itertools; #[cfg(feature = "ipc")] extern crate ethcore_ipc as ipc; diff --git a/util/src/triehash.rs b/util/src/triehash.rs index 13805509a..9884794ca 100644 --- a/util/src/triehash.rs +++ b/util/src/triehash.rs @@ -77,7 +77,9 @@ pub fn ordered_trie_root(input: I) -> H256 /// assert_eq!(trie_root(v), H256::from_str(root).unwrap()); /// } /// ``` -pub fn trie_root(input: Vec<(Vec, Vec)>) -> H256 { +pub fn trie_root(input: I) -> H256 + where I: IntoIterator, Vec)> +{ let gen_input = input // first put elements into btree to sort them and to remove duplicates .into_iter()