diff --git a/ethcore/src/engines/bft.rs b/ethcore/src/engines/bft.rs deleted file mode 100644 index 2131800b7..000000000 --- a/ethcore/src/engines/bft.rs +++ /dev/null @@ -1,296 +0,0 @@ -// 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 . - -//! A blockchain engine that supports a basic, non-BFT proof-of-authority. - -use common::*; -use account_provider::AccountProvider; -use block::*; -use spec::CommonParams; -use engines::Engine; -use evm::Schedule; -use ethjson; - -/// `BFT` params. -#[derive(Debug)] -pub struct BFTParams { - /// Gas limit divisor. - pub gas_limit_bound_divisor: U256, - /// Block duration. - pub duration_limit: u64, - /// Validators. - pub validators: Vec
, - /// Number of validators. - pub validator_n: usize, - /// Precommit step votes. - precommits: RwLock>> -} - -impl From for BFTParams { - fn from(p: ethjson::spec::BFTParams) -> Self { - let val = p.validators.into_iter().map(Into::into).collect::>(); - BFTParams { - gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), - duration_limit: p.duration_limit.into(), - validator_n: val.len(), - validators: val, - precommits: RwLock::new(HashMap::new()) - } - } -} - -/// Engine using `BFT` consensus algorithm, suitable for EVM chain. -pub struct BFT { - params: CommonParams, - our_params: BFTParams, - builtins: BTreeMap, -} - -impl BFT { - /// Create a new instance of BFT engine - pub fn new(params: CommonParams, our_params: BFTParams, builtins: BTreeMap) -> Self { - BFT { - params: params, - our_params: our_params, - builtins: builtins, - } - } - - fn add_precommit(&self, bare_hash: &H256, signature: &Signature) { - if let Some(mut precommits) = self.our_params.precommits.write().get_mut(bare_hash) { - precommits.insert(signature.clone()); - } else { - let mut new = HashSet::new(); - new.insert(signature.clone()); - assert!(self.our_params.precommits.write().insert(bare_hash.clone(), new).is_none()); - } - } - - fn check_precommit(&self, bare_hash: &H256, signature: &Signature) -> result::Result<(), Error> { - let signer = Address::from(try!(ec::recover(&signature, bare_hash)).sha3()); - match self.our_params.validators.contains(&signer) { - false => try!(Err(BlockError::InvalidSeal)), - true => Ok(()), - } - } - - fn supermajority(&self) -> usize { 2*self.our_params.validator_n/3 } - - fn signatures_seal(&self, signatures: &HashSet) -> Vec { - signatures.iter().map(|sig| encode(&(&*sig as &[u8])).to_vec()).collect() - } -} - -impl Engine for BFT { - fn name(&self) -> &str { "BFT" } - fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) } - /// Possibly signatures of all validators. - fn seal_fields(&self) -> usize { self.our_params.validator_n } - - fn params(&self) -> &CommonParams { &self.params } - fn builtins(&self) -> &BTreeMap { &self.builtins } - - /// Additional engine-specific information for the user/developer concerning `header`. - fn extra_info(&self, _header: &Header) -> HashMap { hash_map!["signature".to_owned() => "TODO".to_owned()] } - - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - Schedule::new_homestead() - } - - fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { - header.difficulty = parent.difficulty; - header.gas_limit = { - let gas_limit = parent.gas_limit; - let bound_divisor = self.our_params.gas_limit_bound_divisor; - if gas_limit < gas_floor_target { - min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) - } else { - max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into()) - } - }; - header.note_dirty(); - } - - /// Apply the block reward on finalisation of the block. - /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current). - fn on_close_block(&self, _block: &mut ExecutedBlock) {} - - /// Attempt to seal the block internally using all available signatures. - /// - /// None is returned if not enough signatures can be collected. - fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option> { - let hash = block.header().bare_hash(); - let guard = self.our_params.precommits.read(); - let signatures = guard.get(&hash).unwrap_or(return None); - let threshold = self.supermajority(); - match (signatures.len(), accounts) { - (v, Some(ap)) if v == threshold-1 => { - // account should be pernamently unlocked, otherwise signing will fail - if let Ok(signature) = ap.sign(*block.header().author(), hash) { - self.add_precommit(&hash, &signature.into()); - Some(self.signatures_seal(signatures)) - } else { - trace!(target: "bft", "generate_seal: FAIL: secret key unavailable"); - None - } - }, - (v, _) if v < threshold => None, - _ => Some(block.header().seal.clone()), - } - } - - fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { - // check the seal fields. - // TODO: pull this out into common code. - if header.seal.len() != self.seal_fields() { - return Err(From::from(BlockError::InvalidSealArity( - Mismatch { expected: self.seal_fields(), found: header.seal.len() } - ))); - } - Ok(()) - } - - fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { - let hash = header.bare_hash(); - let threshold = self.supermajority(); - let guard = self.our_params.precommits.read(); - let mut signatures = guard.get(&hash).unwrap_or(try!(Err(BlockError::InvalidSeal))); - if signatures.len() > threshold { return Ok(()) } - // Count all valid precommits. - for seal_field in header.seal() { - let sig = try!(UntrustedRlp::new(&seal_field).as_val::()); - if !signatures.contains(&sig) || self.check_precommit(&hash, &sig).is_ok() { - trace!(target: "bft", "verify_block_unordered: new validator vote found"); - self.add_precommit(&hash, &sig); - if signatures.len() > threshold { return Ok(()) } - } - } - try!(Err(BlockError::InvalidSeal)) - } - - fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { - // we should not calculate difficulty for genesis blocks - if header.number() == 0 { - return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); - } - - // Check difficulty is correct given the two timestamps. - if header.difficulty() != parent.difficulty() { - return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() }))) - } - let gas_limit_divisor = self.our_params.gas_limit_bound_divisor; - let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor; - let max_gas = parent.gas_limit + parent.gas_limit / gas_limit_divisor; - if header.gas_limit <= min_gas || header.gas_limit >= max_gas { - return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit }))); - } - Ok(()) - } - - fn verify_transaction_basic(&self, t: &SignedTransaction, _header: &Header) -> result::Result<(), Error> { - try!(t.check_low_s()); - Ok(()) - } - - fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> { - t.sender().map(|_|()) // Perform EC recovery and cache sender - } -} - - -#[cfg(test)] -mod tests { - use common::*; - use block::*; - use tests::helpers::*; - use account_provider::AccountProvider; - use spec::Spec; - - /// Create a new test chain spec with `BFT` consensus engine. - fn new_test_authority() -> Spec { Spec::load(include_bytes!("../../res/test_authority.json")) } - - #[test] - fn has_valid_metadata() { - let engine = new_test_authority().engine; - assert!(!engine.name().is_empty()); - assert!(engine.version().major >= 1); - } - - #[test] - fn can_return_schedule() { - let engine = new_test_authority().engine; - let schedule = engine.schedule(&EnvInfo { - number: 10000000, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); - - assert!(schedule.stack_limit > 0); - } - - #[test] - fn can_do_seal_verification_fail() { - let engine = new_test_authority().engine; - let header: Header = Header::default(); - - let verify_result = engine.verify_block_basic(&header, None); - - match verify_result { - Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, - Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - - #[test] - fn can_do_signature_verification_fail() { - let engine = new_test_authority().engine; - let mut header: Header = Header::default(); - header.set_seal(vec![rlp::encode(&Signature::zero()).to_vec()]); - - let verify_result = engine.verify_block_unordered(&header, None); - - match verify_result { - Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) => {}, - Err(_) => { panic!("should be block difficulty error (got {:?})", verify_result); }, - _ => { panic!("Should be error, got Ok"); }, - } - } - - #[test] - fn can_generate_seal() { - let tap = AccountProvider::transient_provider(); - let addr = tap.insert_account("".sha3(), "").unwrap(); - tap.unlock_account_permanently(addr, "".into()).unwrap(); - - let spec = new_test_authority(); - let engine = &*spec.engine; - let genesis_header = spec.genesis_header(); - let mut db_result = get_temp_journal_db(); - let mut db = db_result.take(); - spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); - let last_hashes = Arc::new(vec![genesis_header.hash()]); - let vm_factory = Default::default(); - let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); - let b = b.close_and_lock(); - let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); - assert!(b.try_seal(engine, seal).is_ok()); - } -} diff --git a/json/src/spec/bft.rs b/json/src/spec/bft.rs deleted file mode 100644 index a5a34c550..000000000 --- a/json/src/spec/bft.rs +++ /dev/null @@ -1,59 +0,0 @@ -// 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 . - -//! Authority params deserialization. - -use uint::Uint; -use hash::Address; - -/// Authority params deserialization. -#[derive(Debug, PartialEq, Deserialize)] -pub struct BFTParams { - /// Gas limit divisor. - #[serde(rename="gasLimitBoundDivisor")] - pub gas_limit_bound_divisor: Uint, - /// Block duration. - #[serde(rename="durationLimit")] - pub duration_limit: Uint, - /// Valid authorities - pub validators: Vec
, -} - -/// Authority engine deserialization. -#[derive(Debug, PartialEq, Deserialize)] -pub struct BFT { - /// Ethash params. - pub params: BFTParams, -} - -#[cfg(test)] -mod tests { - use serde_json; - use spec::bft::BFT; - - #[test] - fn basic_authority_deserialization() { - let s = r#"{ - "params": { - "gasLimitBoundDivisor": "0x0400", - "durationLimit": "0x0d", - "validators" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] - } - }"#; - - let _deserialized: BFT = serde_json::from_str(s).unwrap(); - } -}