add Vote generation
This commit is contained in:
		
							parent
							
								
									cb2c9938a1
								
							
						
					
					
						commit
						096b71feb2
					
				| @ -25,10 +25,12 @@ pub type LogBloom = H2048; | ||||
| pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]); | ||||
| 
 | ||||
| #[cfg_attr(feature="dev", allow(enum_variant_names))] | ||||
| /// Semantic boolean for when a seal/signature is included.
 | ||||
| /// Enum for when a seal/signature is included.
 | ||||
| pub enum Seal { | ||||
| 	/// The seal/signature is included.
 | ||||
| 	With, | ||||
| 	/// The seal/signature is not included.
 | ||||
| 	Without, | ||||
| 	/// First N fields of seal are included.
 | ||||
| 	WithSome(usize), | ||||
| } | ||||
|  | ||||
| @ -19,6 +19,7 @@ | ||||
| mod message; | ||||
| mod timeout; | ||||
| mod params; | ||||
| mod vote; | ||||
| 
 | ||||
| use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; | ||||
| use common::*; | ||||
| @ -28,20 +29,22 @@ use account_provider::AccountProvider; | ||||
| use block::*; | ||||
| use spec::CommonParams; | ||||
| use engines::{Engine, EngineError, ProposeCollect}; | ||||
| use blockchain::extras::BlockDetails; | ||||
| use evm::Schedule; | ||||
| use io::IoService; | ||||
| use self::message::ConsensusMessage; | ||||
| use self::timeout::{TimerHandler, NextStep}; | ||||
| use self::params::TendermintParams; | ||||
| use self::vote::Vote; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| enum Step { | ||||
| 	Propose, | ||||
| 	Prevote(ProposeCollect), | ||||
| 	/// Precommit step storing the precommit vote and accumulating seal.
 | ||||
| 	Precommit(ProposeCollect, Seal), | ||||
| 	Precommit(ProposeCollect, Signatures), | ||||
| 	/// Commit step storing a complete valid seal.
 | ||||
| 	Commit(BlockHash, Seal) | ||||
| 	Commit(BlockHash, Signatures) | ||||
| } | ||||
| 
 | ||||
| pub type Height = usize; | ||||
| @ -49,7 +52,7 @@ pub type Round = usize; | ||||
| pub type BlockHash = H256; | ||||
| 
 | ||||
| pub type AtomicMs = AtomicUsize; | ||||
| type Seal = Vec<Bytes>; | ||||
| type Signatures = Vec<Bytes>; | ||||
| 
 | ||||
| /// Engine using `Tendermint` consensus algorithm, suitable for EVM chain.
 | ||||
| pub struct Tendermint { | ||||
| @ -57,6 +60,8 @@ pub struct Tendermint { | ||||
| 	our_params: TendermintParams, | ||||
| 	builtins: BTreeMap<Address, Builtin>, | ||||
| 	timeout_service: IoService<NextStep>, | ||||
| 	/// Address to be used as authority.
 | ||||
| 	authority: RwLock<Address>, | ||||
| 	/// Consensus round.
 | ||||
| 	r: AtomicUsize, | ||||
| 	/// Consensus step.
 | ||||
| @ -77,6 +82,7 @@ impl Tendermint { | ||||
| 				our_params: our_params, | ||||
| 				builtins: builtins, | ||||
| 				timeout_service: IoService::<NextStep>::start().expect("Error creating engine timeout service"), | ||||
| 				authority: RwLock::new(Address::default()), | ||||
| 				r: AtomicUsize::new(0), | ||||
| 				s: RwLock::new(Step::Propose), | ||||
| 				proposer_nonce: AtomicUsize::new(0) | ||||
| @ -86,22 +92,26 @@ impl Tendermint { | ||||
| 		engine | ||||
| 	} | ||||
| 
 | ||||
| 	fn proposer(&self) -> Address { | ||||
| 		let ref p = self.our_params; | ||||
| 		p.validators.get(self.proposer_nonce.load(AtomicOrdering::Relaxed)%p.validator_n).unwrap().clone() | ||||
| 	} | ||||
| 
 | ||||
| 	fn is_proposer(&self, address: &Address) -> bool { | ||||
| 		self.proposer() == *address | ||||
| 		self.is_nonce_proposer(self.proposer_nonce.load(AtomicOrdering::SeqCst), address) | ||||
| 	} | ||||
| 
 | ||||
| 	fn is_validator(&self, address: &Address) -> bool { | ||||
| 		self.our_params.validators.contains(address) | ||||
| 	fn nonce_proposer(&self, proposer_nonce: usize) -> &Address { | ||||
| 		let ref p = self.our_params; | ||||
| 		p.authorities.get(proposer_nonce%p.authority_n).unwrap() | ||||
| 	} | ||||
| 
 | ||||
| 	fn is_nonce_proposer(&self, proposer_nonce: usize, address: &Address) -> bool { | ||||
| 		self.nonce_proposer(proposer_nonce) == address | ||||
| 	} | ||||
| 
 | ||||
| 	fn is_authority(&self, address: &Address) -> bool { | ||||
| 		self.our_params.authorities.contains(address) | ||||
| 	} | ||||
| 
 | ||||
| 	fn new_vote(&self, proposal: BlockHash) -> ProposeCollect { | ||||
| 		ProposeCollect::new(proposal, | ||||
| 							self.our_params.validators.iter().cloned().collect(), | ||||
| 							self.our_params.authorities.iter().cloned().collect(), | ||||
| 							self.threshold()) | ||||
| 	} | ||||
| 
 | ||||
| @ -192,7 +202,7 @@ impl Tendermint { | ||||
| 	} | ||||
| 
 | ||||
| 	fn threshold(&self) -> usize { | ||||
| 		self.our_params.validator_n*2/3 | ||||
| 		self.our_params.authority_n*2/3 | ||||
| 	} | ||||
| 
 | ||||
| 	fn next_timeout(&self) -> u64 { | ||||
| @ -203,8 +213,8 @@ impl Tendermint { | ||||
| impl Engine for Tendermint { | ||||
| 	fn name(&self) -> &str { "Tendermint" } | ||||
| 	fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) } | ||||
| 	/// Possibly signatures of all validators.
 | ||||
| 	fn seal_fields(&self) -> usize { 2 } | ||||
| 	/// (consensus round, proposal signature, authority signatures)
 | ||||
| 	fn seal_fields(&self) -> usize { 3 } | ||||
| 
 | ||||
| 	fn params(&self) -> &CommonParams { &self.params } | ||||
| 	fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins } | ||||
| @ -229,34 +239,62 @@ impl Engine for Tendermint { | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	/// Apply the block reward on finalisation of the block.
 | ||||
| 	/// Get the address to be used as authority.
 | ||||
| 	fn on_new_block(&self, block: &mut ExecutedBlock) { | ||||
| 		if let Some(mut authority) = self.authority.try_write()	{ | ||||
| 			*authority = *block.header().author() | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// Set author to proposer.
 | ||||
| 	/// 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<Vec<Bytes>> { | ||||
| 		self.s.try_read().and_then(|s| match *s { | ||||
| 			Step::Commit(hash, ref seal) if hash == block.header().bare_hash() => Some(seal.clone()), | ||||
| 			_ => None, | ||||
| 		}) | ||||
| 	fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> { | ||||
| 		if let (Some(ap), Some(step)) = (accounts, self.s.try_read()) { | ||||
| 			let header = block.header(); | ||||
| 			let author = header.author(); | ||||
| 			match *step { | ||||
| 				Step::Commit(hash, ref seal) if hash == header.bare_hash() => | ||||
| 					// Commit the block using a complete signature set.
 | ||||
| 					return Some(seal.clone()), | ||||
| 				Step::Propose if self.is_proposer(header.author()) => | ||||
| 					// Seal block with propose signature.
 | ||||
| 					if let Some(proposal) = Vote::propose(header, &ap) { | ||||
| 						return Some(vec![::rlp::encode(&proposal).to_vec(), Vec::new()]) | ||||
| 					} else { | ||||
| 						trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable"); | ||||
| 					}, | ||||
| 				_ => {}, | ||||
| 			} | ||||
| 		} else { | ||||
| 			trace!(target: "basicauthority", "generate_seal: FAIL: accounts not provided"); | ||||
| 		} | ||||
| 		None | ||||
| 	} | ||||
| 
 | ||||
| 	fn handle_message(&self, sender: Address, signature: H520, message: UntrustedRlp) -> Result<Bytes, Error> { | ||||
| 		let c: ConsensusMessage = try!(message.as_val()); | ||||
| 		println!("{:?}", c); | ||||
| 		let message: ConsensusMessage = try!(message.as_val()); | ||||
| 		try!(Err(EngineError::UnknownStep)) | ||||
| 		//match message {
 | ||||
| 		//	ConsensusMessage::Prevote 
 | ||||
| 		//}
 | ||||
| 
 | ||||
| 
 | ||||
| 		// Check if correct round.
 | ||||
| 		if self.r.load(AtomicOrdering::Relaxed) != try!(message.val_at(0)) { | ||||
| 			try!(Err(EngineError::WrongRound)) | ||||
| 		} | ||||
| 		//if self.r.load(AtomicOrdering::Relaxed) != try!(message.val_at(0)) {
 | ||||
| 		//	try!(Err(EngineError::WrongRound))
 | ||||
| 		//}
 | ||||
| 		// Handle according to step.
 | ||||
| 		match try!(message.val_at(1)) { | ||||
| 			0u8 if self.is_proposer(&sender) => self.propose_message(try!(message.at(2))), | ||||
| 			1 if self.is_validator(&sender) => self.prevote_message(sender, try!(message.at(2))), | ||||
| 			2 if self.is_validator(&sender) => self.precommit_message(sender, signature, try!(message.at(2))), | ||||
| 			_ => try!(Err(EngineError::UnknownStep)), | ||||
| 		} | ||||
| //		match try!(message.val_at(1)) {
 | ||||
| //			0u8 if self.is_proposer(&sender) => self.propose_message(try!(message.at(2))),
 | ||||
| //			1 if self.is_authority(&sender) => self.prevote_message(sender, try!(message.at(2))),
 | ||||
| //			2 if self.is_authority(&sender) => self.precommit_message(sender, signature, try!(message.at(2))),
 | ||||
| //			_ => try!(Err(EngineError::UnknownStep)),
 | ||||
| //		}
 | ||||
| 	} | ||||
| 
 | ||||
| 	fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { | ||||
| @ -270,18 +308,19 @@ impl Engine for Tendermint { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// Also transitions to Prevote if verifying Proposal.
 | ||||
| 	fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { | ||||
| 		let to_address = |b: &Vec<u8>| { | ||||
| 			let sig: H520 = try!(UntrustedRlp::new(b.as_slice()).as_val()); | ||||
| 			Ok(public_to_address(&try!(recover(&sig.into(), &header.bare_hash())))) | ||||
| 		}; | ||||
| 		let validator_set = self.our_params.validators.iter().cloned().collect(); | ||||
| 		let authority_set = self.our_params.authorities.iter().cloned().collect(); | ||||
| 		let seal_set = try!(header | ||||
| 							.seal() | ||||
| 							.iter() | ||||
| 							.map(to_address) | ||||
| 							.collect::<Result<HashSet<_>, Error>>()); | ||||
| 		if seal_set.intersection(&validator_set).count() <= self.threshold() { | ||||
| 		if seal_set.intersection(&authority_set).count() <= self.threshold() { | ||||
| 			try!(Err(BlockError::InvalidSeal)) | ||||
| 		} else { | ||||
| 			Ok(()) | ||||
| @ -315,6 +354,10 @@ impl Engine for Tendermint { | ||||
| 	fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> { | ||||
| 		t.sender().map(|_|()) // Perform EC recovery and cache sender
 | ||||
| 	} | ||||
| 
 | ||||
| 	fn is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool { | ||||
| 		new_header.seal().get(1).expect("Tendermint seal should have two elements.").len() > best_header.seal().get(1).expect("Tendermint seal should have two elements.").len() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| @ -465,9 +508,9 @@ mod tests { | ||||
| 		let tender = Tendermint::new(engine.params().clone(), TendermintParams::default(), BTreeMap::new()); | ||||
| 
 | ||||
| 		let genesis_header = spec.genesis_header(); | ||||
| 		let mut db_result = get_temp_journal_db(); | ||||
| 		let mut db_result = get_temp_state_db(); | ||||
| 		let mut db = db_result.take(); | ||||
| 		spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); | ||||
| 		spec.ensure_db_good(&mut db).unwrap(); | ||||
| 		let last_hashes = Arc::new(vec![genesis_header.hash()]); | ||||
| 		let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap(); | ||||
| 		let b = b.close_and_lock(); | ||||
| @ -484,8 +527,8 @@ mod tests { | ||||
| 		let tap = AccountProvider::transient_provider(); | ||||
| 		let r = 0; | ||||
| 
 | ||||
| 		let not_validator_addr = tap.insert_account("101".sha3(), "101").unwrap(); | ||||
| 		assert!(propose_default(&engine, r, not_validator_addr).is_err()); | ||||
| 		let not_authority_addr = tap.insert_account("101".sha3(), "101").unwrap(); | ||||
| 		assert!(propose_default(&engine, r, not_authority_addr).is_err()); | ||||
| 
 | ||||
| 		let not_proposer_addr = tap.insert_account("0".sha3(), "0").unwrap(); | ||||
| 		assert!(propose_default(&engine, r, not_proposer_addr).is_err()); | ||||
|  | ||||
							
								
								
									
										63
									
								
								ethcore/src/engines/tendermint/vote.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								ethcore/src/engines/tendermint/vote.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| // 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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| //! Tendermint block seal.
 | ||||
| 
 | ||||
| use common::{H256, Address, H520, Header}; | ||||
| use util::Hashable; | ||||
| use account_provider::AccountProvider; | ||||
| use rlp::{View, DecoderError, Decodable, Decoder, Encodable, RlpStream, Stream}; | ||||
| use basic_types::Seal; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct Vote { | ||||
| 	signature: H520 | ||||
| } | ||||
| 
 | ||||
| fn message(header: &Header) -> H256 { | ||||
| 	header.rlp(Seal::WithSome(1)).sha3() | ||||
| } | ||||
| 
 | ||||
| impl Vote { | ||||
| 	fn new(signature: H520) -> Vote { Vote { signature: signature }} | ||||
| 
 | ||||
| 	/// Try to use the author address to create a vote.
 | ||||
| 	pub fn propose(header: &Header, accounts: &AccountProvider) -> Option<Vote> { | ||||
| 		accounts.sign(*header.author(), message(&header)).ok().map(Into::into).map(Self::new) | ||||
| 	} | ||||
| 	
 | ||||
| 	/// Use any unlocked validator account to create a vote.
 | ||||
| 	pub fn validate(header: &Header, accounts: &AccountProvider, validator: Address) -> Option<Vote> { | ||||
| 		accounts.sign(validator, message(&header)).ok().map(Into::into).map(Self::new) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Decodable for Vote { | ||||
| 	fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { | ||||
| 		let rlp = decoder.as_rlp(); | ||||
| 		if decoder.as_raw().len() != try!(rlp.payload_info()).total() { | ||||
| 			return Err(DecoderError::RlpIsTooBig); | ||||
| 		} | ||||
| 		rlp.as_val().map(Self::new) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Encodable for Vote { | ||||
| 	fn rlp_append(&self, s: &mut RlpStream) { | ||||
| 		let Vote { ref signature } = *self; | ||||
| 		s.append(signature); | ||||
| 	} | ||||
| } | ||||
| @ -226,7 +226,8 @@ impl Header { | ||||
| 	// TODO: make these functions traity
 | ||||
| 	/// Place this header into an RLP stream `s`, optionally `with_seal`.
 | ||||
| 	pub fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) { | ||||
| 		s.begin_list(13 + match with_seal { Seal::With => self.seal.len(), _ => 0 }); | ||||
| 		let seal_n = match with_seal { Seal::With => self.seal.len(), Seal::WithSome(n) => n, _ => 0 }; | ||||
| 		s.begin_list(13 + seal_n); | ||||
| 		s.append(&self.parent_hash); | ||||
| 		s.append(&self.uncles_hash); | ||||
| 		s.append(&self.author); | ||||
| @ -240,10 +241,8 @@ impl Header { | ||||
| 		s.append(&self.gas_used); | ||||
| 		s.append(&self.timestamp); | ||||
| 		s.append(&self.extra_data); | ||||
| 		if let Seal::With = with_seal { | ||||
| 			for b in &self.seal { | ||||
| 				s.append_raw(b, 1); | ||||
| 			} | ||||
| 		for b in self.seal.iter().take(seal_n) { | ||||
| 			s.append_raw(b, 1); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user