diff --git a/Cargo.lock b/Cargo.lock index 20887561b..3298887d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -340,7 +340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "elastic-array" version = "0.7.0" -source = "git+https://github.com/paritytech/elastic-array?branch=0.7.0#970a11eca8a6b3591b476155c5896b4757b5c7b8" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -713,7 +713,7 @@ version = "1.7.0" dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.7.0 (git+https://github.com/paritytech/elastic-array?branch=0.7.0)", + "elastic-array 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)", "ethcore-bigint 0.1.2", @@ -2018,7 +2018,7 @@ name = "rlp" version = "0.1.0" dependencies = [ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.7.0 (git+https://github.com/paritytech/elastic-array?branch=0.7.0)", + "elastic-array 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bigint 0.1.2", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2790,7 +2790,7 @@ dependencies = [ "checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8" "checksum dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" "checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91" -"checksum elastic-array 0.7.0 (git+https://github.com/paritytech/elastic-array?branch=0.7.0)" = "" +"checksum elastic-array 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71a64decd4b8cd06654a4e643c45cb558ad554abbffd82a7e16e34f45f51b605" "checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83" "checksum eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)" = "" "checksum ethabi 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63df67d0af5e3cb906b667ca1a6e00baffbed87d0d8f5f78468a1f5eb3a66345" diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 00420e58f..79169fd82 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -810,9 +810,11 @@ impl BlockChain { } }; - transitions.candidates.push(transition); - - batch.write(db::COL_EXTRA, &epoch_num, &transitions); + // ensure we don't write any duplicates. + if transitions.candidates.iter().find(|c| c.block_hash == transition.block_hash).is_none() { + transitions.candidates.push(transition); + batch.write(db::COL_EXTRA, &epoch_num, &transitions); + } } /// Add a child to a given block. Assumes that the block hash is in diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index ab9a07691..26d823dce 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -49,7 +49,7 @@ use evm::{Factory as EvmFactory, Schedule}; use executive::{Executive, Executed, TransactOptions, contract_address}; use factory::Factories; use futures::{future, Future}; -use header::BlockNumber; +use header::{BlockNumber, Header}; use io::*; use log_entry::LocalizedLogEntry; use miner::{Miner, MinerService, TransactionImportResult}; @@ -247,17 +247,29 @@ impl Client { exit_handler: Mutex::new(None), }); + // prune old states. { let state_db = client.state_db.lock().boxed_clone(); let chain = client.chain.read(); client.prune_ancient(state_db, &chain)?; } + // ensure genesis epoch proof in the DB. + { + let mut batch = DBTransaction::new(); + let chain = client.chain.read(); + client.generate_epoch_proof(&mut batch, &spec.genesis_header(), 0, &*chain); + client.db.read().write_buffered(batch); + } + if let Some(reg_addr) = client.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()) { trace!(target: "client", "Found registrar at {}", reg_addr); let registrar = Registry::new(reg_addr); *client.registrar.lock() = Some(registrar); } + + // ensure buffered changes are flushed. + client.db.read().flush().map_err(ClientError::Database)?; Ok(client) } @@ -581,7 +593,7 @@ impl Client { let entering_new_epoch = { use engines::EpochChange; match self.engine.is_epoch_end(block.header(), Some(block_data), Some(&receipts)) { - EpochChange::Yes(e, p) => Some((block.header().clone(), e, p)), + EpochChange::Yes(e, _) => Some((block.header().clone(), e)), EpochChange::No => None, EpochChange::Unsure(_) => { warn!(target: "client", "Detected invalid engine implementation."); @@ -599,48 +611,8 @@ impl Client { state.journal_under(&mut batch, number, hash).expect("DB commit failed"); let route = chain.insert_block(&mut batch, block_data, receipts); - if let Some((header, epoch, expected)) = entering_new_epoch { - use std::cell::RefCell; - use std::collections::BTreeSet; - - debug!(target: "client", "Generating validation proof for block {}", hash); - - // proof is two-part. state items read in lexicographical order, - // and the secondary "proof" part. - let read_values = RefCell::new(BTreeSet::new()); - let block_id = BlockId::Hash(hash.clone()); - let proof = { - let call = |a, d| { - let tx = self.contract_call_tx(block_id, a, d); - let (result, items) = self.prove_transaction(tx, block_id) - .ok_or_else(|| format!("Unable to make call to generate epoch proof."))?; - - read_values.borrow_mut().extend(items); - Ok(result) - }; - - self.engine.epoch_proof(&header, &call) - }; - - match proof { - Ok(proof) => { - if proof != expected { - warn!(target: "client", "Extracted epoch change proof different than expected."); - warn!(target: "client", "Using a custom engine implementation?"); - } - - // insert into database, using the generated proof. - chain.insert_epoch_transition(&mut batch, epoch, EpochTransition { - block_hash: hash.clone(), - proof: proof, - state_proof: read_values.into_inner().into_iter().collect(), - }); - } - Err(e) => { - warn!(target: "client", "Error generating epoch change proof for block {}: {}", hash, e); - warn!(target: "client", "Snapshots generated by this node will be incomplete."); - } - } + if let Some((header, epoch)) = entering_new_epoch { + self.generate_epoch_proof(&mut batch, &header, epoch, &chain); } self.tracedb.read().import(&mut batch, TraceImportRequest { @@ -665,6 +637,46 @@ impl Client { route } + // generate an epoch transition proof at the given block, and write it into the given blockchain. + fn generate_epoch_proof(&self, batch: &mut DBTransaction, header: &Header, epoch_number: u64, chain: &BlockChain) { + use std::cell::RefCell; + use std::collections::BTreeSet; + + let hash = header.hash(); + debug!(target: "client", "Generating validation proof for block {}", hash); + + // proof is two-part. state items read in lexicographical order, + // and the secondary "proof" part. + let read_values = RefCell::new(BTreeSet::new()); + let block_id = BlockId::Hash(hash.clone()); + let proof = { + let call = |a, d| { + let tx = self.contract_call_tx(block_id, a, d); + let (result, items) = self.prove_transaction(tx, block_id) + .ok_or_else(|| format!("Unable to make call to generate epoch proof."))?; + + read_values.borrow_mut().extend(items); + Ok(result) + }; + + self.engine.epoch_proof(&header, &call) + }; + + // insert into database, using the generated proof. + match proof { + Ok(proof) => + chain.insert_epoch_transition(batch, epoch_number, EpochTransition { + block_hash: hash.clone(), + proof: proof, + state_proof: read_values.into_inner().into_iter().collect(), + }), + Err(e) => { + warn!(target: "client", "Error generating epoch change proof for block {}: {}", hash, e); + warn!(target: "client", "Snapshots generated by this node will be incomplete."); + } + } + } + // prune ancient states until below the memory limit or only the minimum amount remain. fn prune_ancient(&self, mut state_db: StateDB, chain: &BlockChain) -> Result<(), ClientError> { let number = match state_db.journal_db().latest_era() {