return errors from constructor
This commit is contained in:
		
							parent
							
								
									dd8ed42270
								
							
						
					
					
						commit
						54e4956345
					
				| @ -19,11 +19,11 @@ | ||||
| mod message; | ||||
| mod timeout; | ||||
| mod params; | ||||
| mod vote; | ||||
| mod vote_collector; | ||||
| 
 | ||||
| use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; | ||||
| use util::*; | ||||
| use basic_types::Seal; | ||||
| use error::{Error, BlockError}; | ||||
| use header::Header; | ||||
| use builtin::Builtin; | ||||
| @ -42,16 +42,14 @@ use io::IoService; | ||||
| use self::message::ConsensusMessage; | ||||
| use self::timeout::{TimerHandler, NextStep}; | ||||
| use self::params::TendermintParams; | ||||
| use self::vote::Vote; | ||||
| use self::vote_collector::VoteCollector; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| enum Step { | ||||
| #[derive(Debug, PartialEq, Eq)] | ||||
| pub enum Step { | ||||
| 	Propose, | ||||
| 	Prevote(ProposeCollect), | ||||
| 	/// Precommit step storing the precommit vote and accumulating seal.
 | ||||
| 	Precommit(ProposeCollect, Signatures), | ||||
| 	/// Commit step storing a complete valid seal.
 | ||||
| 	Commit(BlockHash, Signatures) | ||||
| 	Prevote, | ||||
| 	Precommit, | ||||
| 	Commit | ||||
| } | ||||
| 
 | ||||
| pub type Height = usize; | ||||
| @ -69,43 +67,45 @@ pub struct Tendermint { | ||||
| 	timeout_service: IoService<NextStep>, | ||||
| 	/// Address to be used as authority.
 | ||||
| 	authority: RwLock<Address>, | ||||
| 	/// Blockchain height.
 | ||||
| 	height: AtomicUsize, | ||||
| 	/// Consensus round.
 | ||||
| 	r: AtomicUsize, | ||||
| 	round: AtomicUsize, | ||||
| 	/// Consensus step.
 | ||||
| 	s: RwLock<Step>, | ||||
| 	step: RwLock<Step>, | ||||
| 	/// Current step timeout in ms.
 | ||||
| 	timeout: AtomicMs, | ||||
| 	/// Used to swith proposer.
 | ||||
| 	proposer_nonce: AtomicUsize, | ||||
| 	/// Vote accumulator.
 | ||||
| 	votes: VoteCollector | ||||
| } | ||||
| 
 | ||||
| impl Tendermint { | ||||
| 	/// Create a new instance of Tendermint engine
 | ||||
| 	pub fn new(params: CommonParams, our_params: TendermintParams, builtins: BTreeMap<Address, Builtin>) -> Arc<Self> { | ||||
| 	pub fn new(params: CommonParams, our_params: TendermintParams, builtins: BTreeMap<Address, Builtin>) -> Result<Arc<Self>, Error> { | ||||
| 		let engine = Arc::new( | ||||
| 			Tendermint { | ||||
| 				params: params, | ||||
| 				timeout: AtomicUsize::new(our_params.timeouts.propose), | ||||
| 				our_params: our_params, | ||||
| 				builtins: builtins, | ||||
| 				timeout_service: IoService::<NextStep>::start().expect("Error creating engine timeout service"), | ||||
| 				timeout_service: try!(IoService::<NextStep>::start()), | ||||
| 				authority: RwLock::new(Address::default()), | ||||
| 				r: AtomicUsize::new(0), | ||||
| 				s: RwLock::new(Step::Propose), | ||||
| 				proposer_nonce: AtomicUsize::new(0) | ||||
| 				height: AtomicUsize::new(0), | ||||
| 				round: AtomicUsize::new(0), | ||||
| 				step: RwLock::new(Step::Propose), | ||||
| 				proposer_nonce: AtomicUsize::new(0), | ||||
| 				votes: VoteCollector::new() | ||||
| 			}); | ||||
| 		let handler = TimerHandler::new(Arc::downgrade(&engine)); | ||||
| 		engine.timeout_service.register_handler(Arc::new(handler)).expect("Error creating engine timeout service"); | ||||
| 		engine | ||||
| 	} | ||||
| 
 | ||||
| 	fn is_proposer(&self, address: &Address) -> bool { | ||||
| 		self.is_nonce_proposer(self.proposer_nonce.load(AtomicOrdering::SeqCst), address) | ||||
| 		let handler = TimerHandler { engine: Arc::downgrade(&engine) }; | ||||
| 		try!(engine.timeout_service.register_handler(Arc::new(handler))); | ||||
| 		Ok(engine) | ||||
| 	} | ||||
| 
 | ||||
| 	fn nonce_proposer(&self, proposer_nonce: usize) -> &Address { | ||||
| 		let ref p = self.our_params; | ||||
| 		p.authorities.get(proposer_nonce%p.authority_n).unwrap() | ||||
| 		p.authorities.get(proposer_nonce % p.authority_n).unwrap() | ||||
| 	} | ||||
| 
 | ||||
| 	fn is_nonce_proposer(&self, proposer_nonce: usize, address: &Address) -> bool { | ||||
| @ -123,12 +123,12 @@ impl Tendermint { | ||||
| 	} | ||||
| 
 | ||||
| 	fn to_step(&self, step: Step) { | ||||
| 		let mut guard = self.s.try_write().unwrap(); | ||||
| 		let mut guard = self.step.try_write().unwrap(); | ||||
| 		*guard = step; | ||||
| 	} | ||||
| 
 | ||||
| 	fn to_propose(&self) { | ||||
| 		trace!(target: "tendermint", "step: entering propose"); | ||||
| 		trace!(target: "poa", "step: entering propose"); | ||||
| 		println!("step: entering propose"); | ||||
| 		self.proposer_nonce.fetch_add(1, AtomicOrdering::Relaxed); | ||||
| 		self.to_step(Step::Propose); | ||||
| @ -136,7 +136,7 @@ impl Tendermint { | ||||
| 
 | ||||
| 	fn propose_message(&self, message: UntrustedRlp) -> Result<Bytes, Error> { | ||||
| 		// Check if message is for correct step.
 | ||||
| 		match *self.s.try_read().unwrap() { | ||||
| 		match *self.step.try_read().unwrap() { | ||||
| 			Step::Propose => (), | ||||
| 			_ => try!(Err(EngineError::WrongStep)), | ||||
| 		} | ||||
| @ -146,7 +146,7 @@ impl Tendermint { | ||||
| 	} | ||||
| 
 | ||||
| 	fn to_prevote(&self, proposal: BlockHash) { | ||||
| 		trace!(target: "tendermint", "step: entering prevote"); | ||||
| 		trace!(target: "poa", "step: entering prevote"); | ||||
| 		println!("step: entering prevote"); | ||||
| 		// Proceed to the prevote step.
 | ||||
| 		self.to_step(Step::Prevote(self.new_vote(proposal))); | ||||
| @ -154,8 +154,8 @@ impl Tendermint { | ||||
| 
 | ||||
| 	fn prevote_message(&self, sender: Address, message: UntrustedRlp) -> Result<Bytes, Error> { | ||||
| 		// Check if message is for correct step.
 | ||||
| 		let hash = match *self.s.try_write().unwrap() { | ||||
| 			Step::Prevote(ref mut vote) => { | ||||
| 		let hash = match *self.step.try_write().unwrap() { | ||||
| 			Step::Prevote => { | ||||
| 				// Vote if message is about the right block.
 | ||||
| 				if vote.hash == try!(message.as_val()) { | ||||
| 					vote.vote(sender); | ||||
| @ -178,43 +178,39 @@ impl Tendermint { | ||||
| 	} | ||||
| 
 | ||||
| 	fn to_precommit(&self, proposal: BlockHash) { | ||||
| 		trace!(target: "tendermint", "step: entering precommit"); | ||||
| 		trace!(target: "poa", "step: entering precommit"); | ||||
| 		println!("step: entering precommit"); | ||||
| 		self.to_step(Step::Precommit(self.new_vote(proposal), Vec::new())); | ||||
| 	} | ||||
| 
 | ||||
| 	fn precommit_message(&self, sender: Address, signature: H520, message: UntrustedRlp) -> Result<Bytes, Error> { | ||||
| 		// Check if message is for correct step.
 | ||||
| 		match *self.s.try_write().unwrap() { | ||||
| 			Step::Precommit(ref mut vote, ref mut seal) => { | ||||
| 				// Vote and accumulate seal if message is about the right block.
 | ||||
| 				if vote.hash == try!(message.as_val()) { | ||||
| 					if vote.vote(sender) { seal.push(encode(&signature).to_vec()); } | ||||
| 					// Commit if precommit is won.
 | ||||
| 					if vote.is_won() { self.to_commit(vote.hash.clone(), seal.clone()); } | ||||
| 					Ok(message.as_raw().to_vec()) | ||||
| 				} else { | ||||
| 					try!(Err(EngineError::WrongVote)) | ||||
| 				} | ||||
| 			}, | ||||
| 			_ => try!(Err(EngineError::WrongStep)), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// Move to commit step, when valid block is known and being distributed.
 | ||||
| 	pub fn to_commit(&self, block_hash: H256, seal: Vec<Bytes>) { | ||||
| 		trace!(target: "tendermint", "step: entering commit"); | ||||
| 		trace!(target: "poa", "step: entering commit"); | ||||
| 		println!("step: entering commit"); | ||||
| 		self.to_step(Step::Commit(block_hash, seal)); | ||||
| 	} | ||||
| 
 | ||||
| 	fn threshold(&self) -> usize { | ||||
| 		self.our_params.authority_n*2/3 | ||||
| 		self.our_params.authority_n * 2/3 | ||||
| 	} | ||||
| 
 | ||||
| 	fn next_timeout(&self) -> u64 { | ||||
| 		self.timeout.load(AtomicOrdering::Relaxed) as u64 | ||||
| 	} | ||||
| 
 | ||||
| 	/// Round proposer switching.
 | ||||
| 	fn is_proposer(&self, address: &Address) -> bool { | ||||
| 		self.is_nonce_proposer(self.proposer_nonce.load(AtomicOrdering::SeqCst), address) | ||||
| 	} | ||||
| 
 | ||||
| 	fn is_current(&self, message: &ConsensusMessage) -> bool { | ||||
| 		message.is_step(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst), self.step.load(AtomicOrdering::SeqCst)) 
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /// Block hash including the consensus round.
 | ||||
| fn block_hash(header: &Header) -> H256 { | ||||
| 	header.rlp(Seal::WithSome(1)).sha3() | ||||
| } | ||||
| 
 | ||||
| impl Engine for Tendermint { | ||||
| @ -253,60 +249,79 @@ impl Engine for Tendermint { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// Set author to proposer and set the correct round in the seal.
 | ||||
| 	/// Set the correct round in the seal.
 | ||||
| 	/// 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) { | ||||
| 		
 | ||||
| 	fn on_close_block(&self, block: &mut ExecutedBlock) { | ||||
| 		let round = self.round.load(AtomicOrdering::SeqCst); | ||||
| 		vec![::rlp::encode(&round).to_vec()] | ||||
| 	} | ||||
| 
 | ||||
| 	/// Round proposer switching.
 | ||||
| 	fn is_sealer(&self, address: &Address) -> Option<bool> { | ||||
| 		Some(self.is_proposer(address)) | ||||
| 	} | ||||
| 
 | ||||
| 	/// 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>> { | ||||
| 		if let (Some(ap), Some(step)) = (accounts, self.s.try_read()) { | ||||
| 		if let (Some(ap), Some(step)) = (accounts, self.step.try_read()) { | ||||
| 			let header = block.header(); | ||||
| 			let author = header.author(); | ||||
| 			match *step { | ||||
| 				Step::Commit(hash, ref seal) if hash == header.bare_hash() => | ||||
| 				Step::Commit => { | ||||
| 					// Commit the block using a complete signature set.
 | ||||
| 					return Some(seal.clone()), | ||||
| 				Step::Propose if self.is_proposer(header.author()) => | ||||
| 					let round = self.round.load(AtomicOrdering::SeqCst); | ||||
| 					if let Some((proposer, votes)) = self.votes.seal_signatures(header.number() as Height, round, block_hash(header)).split_first() { | ||||
| 						if votes.len() + 1 > self.threshold() { | ||||
| 							return Some(vec![ | ||||
| 								::rlp::encode(&round).to_vec(), | ||||
| 								::rlp::encode(proposer).to_vec(), | ||||
| 								::rlp::encode(&votes).to_vec() | ||||
| 							]); | ||||
| 						} | ||||
| 					} | ||||
| 				}, | ||||
| 				Step::Propose if self.is_proposer(author) => | ||||
| 					// Seal block with propose signature.
 | ||||
| 					if let Some(proposal) = Vote::propose(header, &ap) { | ||||
| 						return Some(vec![::rlp::encode(&proposal).to_vec(), Vec::new()]) | ||||
| 					
 | ||||
| 					if let Ok(signature) = ap.sign(*author, None, block_hash(header)) { | ||||
| 						return Some(vec![ | ||||
| 							::rlp::encode(&self.round.load(AtomicOrdering::SeqCst)).to_vec(), | ||||
| 							::rlp::encode(&signature.into()).to_vec(), | ||||
| 							Vec::new() | ||||
| 						]) | ||||
| 					} else { | ||||
| 						trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable"); | ||||
| 						trace!(target: "poa", "generate_seal: FAIL: accounts secret key unavailable"); | ||||
| 					}, | ||||
| 				_ => {}, | ||||
| 			} | ||||
| 		} else { | ||||
| 			trace!(target: "basicauthority", "generate_seal: FAIL: accounts not provided"); | ||||
| 			trace!(target: "poa", "generate_seal: FAIL: accounts not provided"); | ||||
| 		} | ||||
| 		None | ||||
| 	} | ||||
| 
 | ||||
| 	fn handle_message(&self, sender: Address, signature: H520, message: UntrustedRlp) -> Result<Bytes, Error> { | ||||
| 		let message: ConsensusMessage = try!(message.as_val()); | ||||
| 
 | ||||
| 		if self.is_authority(&sender) { | ||||
| 			//match message {
 | ||||
| 			//		ConsensusMessage::Prevote 
 | ||||
| 			//}
 | ||||
| 	fn handle_message(&self, rlp: UntrustedRlp) -> Result<Bytes, Error> { | ||||
| 		let message: ConsensusMessage = try!(rlp.as_val()); | ||||
| 		let sender = public_to_address(&try!(recover(&message.signature.into(), &try!(rlp.at(1)).as_raw().sha3()))); | ||||
| 		// TODO: Do not admit old messages.
 | ||||
| 		if !self.is_authority(&sender) { | ||||
| 			try!(Err(BlockError::InvalidSeal)); | ||||
| 		} | ||||
| 
 | ||||
| 		try!(Err(EngineError::UnknownStep)) | ||||
| 
 | ||||
| 		// Check if correct round.
 | ||||
| 		//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_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)),
 | ||||
| //		}
 | ||||
| 		// Check if the message affects the current step.
 | ||||
| 		if self.is_current(message) { | ||||
| 				match self.step.load(AtomicOrdering::SeqCst) { | ||||
| 					Step::Prevote => { | ||||
| 						let votes = aligned_signatures(message); | ||||
| 						if votes.len() > self.threshold() { | ||||
| 						} | ||||
| 					}, | ||||
| 					Step::Precommit => , | ||||
| 				} | ||||
| 		} | ||||
| 		self.votes.vote(message, sender); | ||||
| 	} | ||||
| 
 | ||||
| 	fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { | ||||
| @ -322,21 +337,28 @@ 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 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(&authority_set).count() <= self.threshold() { | ||||
| 		let proposal_signature: H520 = try!(UntrustedRlp::new(header.seal()[1].as_slice()).as_val()); | ||||
| 		let proposer = public_to_address(&try!(recover(&proposal_signature.into(), &block_hash(header)))); | ||||
| 		if !self.is_proposer(&proposer) { | ||||
| 			try!(Err(BlockError::InvalidSeal)) | ||||
| 		} else { | ||||
| 			Ok(()) | ||||
| 		} | ||||
| 		let proposal = ConsensusMessage { | ||||
| 			signature: proposal_signature, | ||||
| 			height: header.number() as usize, | ||||
| 			round: try!(UntrustedRlp::new(header.seal()[0].as_slice()).as_val()), | ||||
| 			step: Step::Propose, | ||||
| 			block_hash: Some(block_hash(header)) | ||||
| 		}; | ||||
| 		self.votes.vote(proposal, proposer); | ||||
| 		let votes_rlp = UntrustedRlp::new(&header.seal()[2]); | ||||
| 		for rlp in votes_rlp.iter() { | ||||
| 			let sig: H520 = try!(rlp.as_val()); | ||||
| 			let address = public_to_address(&try!(recover(&sig.into(), &block_hash(header)))); | ||||
| 			if !self.our_params.authorities.contains(a) { | ||||
| 				try!(Err(BlockError::InvalidSeal)) | ||||
| 			} | ||||
| 		} | ||||
| 		Ok(()) | ||||
| 	} | ||||
| 
 | ||||
| 	fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { | ||||
| @ -368,7 +390,9 @@ impl Engine for Tendermint { | ||||
| 	} | ||||
| 
 | ||||
| 	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() | ||||
| 		let new_signatures = new_header.seal().get(2).expect("Tendermint seal should have three elements.").len(); | ||||
| 		let best_signatures = best_header.seal().get(2).expect("Tendermint seal should have three elements.").len(); | ||||
| 		new_signatures > best_signatures | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -377,7 +401,7 @@ mod tests { | ||||
| 	use std::thread::sleep; | ||||
| 	use std::time::{Duration}; | ||||
| 	use util::*; | ||||
| 	use rlp::{UntrustedRlp, RlpStream, Stream, View, encode}; | ||||
| 	use rlp::{UntrustedRlp, RlpStream, Stream, View}; | ||||
| 	use block::*; | ||||
| 	use error::{Error, BlockError}; | ||||
| 	use header::Header; | ||||
| @ -416,11 +440,11 @@ mod tests { | ||||
| 
 | ||||
| 		let v0 = tap.insert_account("0".sha3(), "0").unwrap(); | ||||
| 		let sig0 = tap.sign(v0, Some("0".into()), header.bare_hash()).unwrap(); | ||||
| 		seal.push(encode(&(&*sig0 as &[u8])).to_vec()); | ||||
| 		seal.push(::rlp::encode(&(&*sig0 as &[u8])).to_vec()); | ||||
| 
 | ||||
| 		let v1 = tap.insert_account("1".sha3(), "1").unwrap(); | ||||
| 		let sig1 = tap.sign(v1, Some("1".into()), header.bare_hash()).unwrap(); | ||||
| 		seal.push(encode(&(&*sig1 as &[u8])).to_vec()); | ||||
| 		seal.push(::rlp::encode(&(&*sig1 as &[u8])).to_vec()); | ||||
| 		seal | ||||
| 	} | ||||
| 
 | ||||
| @ -475,7 +499,7 @@ mod tests { | ||||
| 
 | ||||
| 		let v1 = tap.insert_account("0".sha3(), "0").unwrap(); | ||||
| 		let sig1 = tap.sign(v1, Some("0".into()), header.bare_hash()).unwrap(); | ||||
| 		seal.push(encode(&(&*sig1 as &[u8])).to_vec()); | ||||
| 		seal.push(::rlp::encode(&(&*sig1 as &[u8])).to_vec()); | ||||
| 
 | ||||
| 		header.set_seal(seal.clone()); | ||||
| 
 | ||||
| @ -484,7 +508,7 @@ mod tests { | ||||
| 
 | ||||
| 		let v2 = tap.insert_account("101".sha3(), "101").unwrap(); | ||||
| 		let sig2 = tap.sign(v2, Some("101".into()), header.bare_hash()).unwrap(); | ||||
| 		seal.push(encode(&(&*sig2 as &[u8])).to_vec()); | ||||
| 		seal.push(::rlp::encode(&(&*sig2 as &[u8])).to_vec()); | ||||
| 
 | ||||
| 		header.set_seal(seal); | ||||
| 
 | ||||
|  | ||||
| @ -144,7 +144,7 @@ impl Spec { | ||||
| 			ethjson::spec::Engine::InstantSeal => Arc::new(InstantSeal::new(params, builtins)), | ||||
| 			ethjson::spec::Engine::Ethash(ethash) => Arc::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins)), | ||||
| 			ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(params, From::from(basic_authority.params), builtins)), | ||||
| 			ethjson::spec::Engine::Tendermint(tendermint) => Tendermint::new(params, From::from(tendermint.params), builtins), | ||||
| 			ethjson::spec::Engine::Tendermint(tendermint) => Tendermint::new(params, From::from(tendermint.params), builtins).expect("Failed to start the Tendermint consensus engine."), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user