lock rounds

This commit is contained in:
keorn 2016-11-18 12:27:00 +00:00
parent c62795d09b
commit e90d814193
3 changed files with 53 additions and 31 deletions

View File

@ -78,14 +78,14 @@ pub struct Tendermint {
proposer_nonce: AtomicUsize, proposer_nonce: AtomicUsize,
/// Vote accumulator. /// Vote accumulator.
votes: VoteCollector, votes: VoteCollector,
/// Proposed block held until seal is gathered.
proposed_block: Mutex<Option<ExecutedBlock>>,
/// Channel for updating the sealing. /// Channel for updating the sealing.
message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>, message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>,
/// Used to sign messages and proposals. /// Used to sign messages and proposals.
account_provider: Mutex<Option<Arc<AccountProvider>>>, account_provider: Mutex<Option<Arc<AccountProvider>>>,
/// Message for the last PoLC. /// Message for the last PoLC.
last_lock: RwLock<Option<ConsensusMessage>>, lock_change: RwLock<Option<ConsensusMessage>>,
/// Last lock round.
last_lock: AtomicUsize,
/// Bare hash of the proposed block, used for seal submission. /// Bare hash of the proposed block, used for seal submission.
proposal: RwLock<Option<H256>> proposal: RwLock<Option<H256>>
} }
@ -105,10 +105,10 @@ impl Tendermint {
step: RwLock::new(Step::Propose), step: RwLock::new(Step::Propose),
proposer_nonce: AtomicUsize::new(0), proposer_nonce: AtomicUsize::new(0),
votes: VoteCollector::new(), votes: VoteCollector::new(),
proposed_block: Mutex::new(None),
message_channel: Mutex::new(None), message_channel: Mutex::new(None),
account_provider: Mutex::new(None), account_provider: Mutex::new(None),
last_lock: RwLock::new(None), lock_change: RwLock::new(None),
last_lock: AtomicUsize::new(0),
proposal: RwLock::new(None) proposal: RwLock::new(None)
}); });
let handler = TransitionHandler { engine: Arc::downgrade(&engine) }; let handler = TransitionHandler { engine: Arc::downgrade(&engine) };
@ -169,29 +169,41 @@ impl Tendermint {
self.update_sealing() self.update_sealing()
}, },
Step::Prevote => { Step::Prevote => {
self.broadcast_message(None) let should_unlock = |lock_change_round| {
self.last_lock.load(AtomicOrdering::SeqCst) < lock_change_round
&& lock_change_round < self.round.load(AtomicOrdering::SeqCst)
};
let block_hash = match *self.lock_change.read() {
Some(ref m) if should_unlock(m.round) => self.proposal.read().clone(),
Some(ref m) => m.block_hash,
None => None,
};
self.broadcast_message(block_hash)
}, },
Step::Precommit => { Step::Precommit => {
let block_hash = match *self.last_lock.read() { let block_hash = match *self.lock_change.read() {
Some(ref m) => None, Some(ref m) if self.is_round(m) => {
None => None, self.last_lock.store(m.round, AtomicOrdering::SeqCst);
m.block_hash
},
_ => None,
}; };
self.broadcast_message(block_hash); self.broadcast_message(block_hash);
}, },
Step::Commit => { Step::Commit => {
// Commit the block using a complete signature set. // Commit the block using a complete signature set.
let round = self.round.load(AtomicOrdering::SeqCst); let round = self.round.load(AtomicOrdering::SeqCst);
if let Some((proposer, votes)) = self.votes.seal_signatures(self.height.load(AtomicOrdering::SeqCst), round, *self.proposal.read()) { if let Some(seal) = self.votes.seal_signatures(self.height.load(AtomicOrdering::SeqCst), round, *self.proposal.read()) {
let seal = vec![ let seal = vec![
::rlp::encode(&round).to_vec(), ::rlp::encode(&round).to_vec(),
::rlp::encode(proposer).to_vec(), ::rlp::encode(&seal.proposal).to_vec(),
::rlp::encode(&votes).to_vec() ::rlp::encode(&seal.votes).to_vec()
]; ];
if let Some(block_hash) = *self.proposal.read() { if let Some(block_hash) = *self.proposal.read() {
self.submit_seal(block_hash, seal); self.submit_seal(block_hash, seal);
} }
} }
*self.last_lock.write() = None; *self.lock_change.write() = None;
}, },
} }
} }
@ -218,10 +230,14 @@ impl Tendermint {
self.is_nonce_proposer(self.proposer_nonce.load(AtomicOrdering::SeqCst), address) self.is_nonce_proposer(self.proposer_nonce.load(AtomicOrdering::SeqCst), address)
} }
fn is_current(&self, message: &ConsensusMessage) -> bool { fn is__height(&self, message: &ConsensusMessage) -> bool {
message.is_height(self.height.load(AtomicOrdering::SeqCst)) message.is_height(self.height.load(AtomicOrdering::SeqCst))
} }
fn is_round(&self, message: &ConsensusMessage) -> bool {
message.is_round(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst))
}
fn has_enough_any_votes(&self) -> bool { fn has_enough_any_votes(&self) -> bool {
self.votes.count_step_votes(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst), *self.step.read()) > self.threshold() self.votes.count_step_votes(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst), *self.step.read()) > self.threshold()
} }
@ -332,17 +348,17 @@ impl Engine for Tendermint {
// Check if the message is known. // Check if the message is known.
if self.votes.vote(message.clone(), sender).is_none() { if self.votes.vote(message.clone(), sender).is_none() {
let is_newer_than_lock = match *self.last_lock.read() { let is_newer_than_lock = match *self.lock_change.read() {
Some(ref lock) => &message > lock, Some(ref lock) => &message > lock,
None => true, None => true,
}; };
if is_newer_than_lock if is_newer_than_lock
&& message.step == Step::Prevote && message.step == Step::Prevote
&& self.has_enough_aligned_votes(&message) { && self.has_enough_aligned_votes(&message) {
*self.last_lock.write() = Some(message.clone()); *self.lock_change.write() = Some(message.clone());
} }
// Check if it can affect step transition. // Check if it can affect step transition.
if self.is_current(&message) { if self.is__height(&message) {
let next_step = match *self.step.read() { let next_step = match *self.step.read() {
Step::Precommit if self.has_enough_aligned_votes(&message) => { Step::Precommit if self.has_enough_aligned_votes(&message) => {
if message.block_hash.is_none() { if message.block_hash.is_none() {

View File

@ -91,6 +91,7 @@ impl IoHandler<Step> for TransitionHandler {
}, },
Step::Commit => { Step::Commit => {
set_timeout(io, engine.our_params.timeouts.propose); set_timeout(io, engine.our_params.timeouts.propose);
engine.last_lock.store(0, AtomicOrdering::SeqCst);
engine.round.store(0, AtomicOrdering::SeqCst); engine.round.store(0, AtomicOrdering::SeqCst);
engine.height.fetch_add(1, AtomicOrdering::SeqCst); engine.height.fetch_add(1, AtomicOrdering::SeqCst);
Some(Step::Propose) Some(Step::Propose)

View File

@ -27,6 +27,11 @@ pub struct VoteCollector {
votes: RwLock<BTreeMap<ConsensusMessage, Address>> votes: RwLock<BTreeMap<ConsensusMessage, Address>>
} }
pub struct SealSignatures {
pub proposal: H520,
pub votes: Vec<H520>
}
impl VoteCollector { impl VoteCollector {
pub fn new() -> VoteCollector { pub fn new() -> VoteCollector {
VoteCollector { votes: RwLock::new(BTreeMap::new()) } VoteCollector { votes: RwLock::new(BTreeMap::new()) }
@ -36,29 +41,29 @@ impl VoteCollector {
self.votes.write().insert(message, voter) self.votes.write().insert(message, voter)
} }
pub fn seal_signatures(&self, height: Height, round: Round, block_hash: Option<H256>) -> Option<(&H520, &[H520])> { pub fn seal_signatures(&self, height: Height, round: Round, block_hash: Option<H256>) -> Option<SealSignatures> {
self.votes let guard = self.votes.read();
.read()
.keys()
.cloned()
// Get only Propose and Precommits. // Get only Propose and Precommits.
let mut correct_signatures = guard.keys()
.filter(|m| m.is_aligned(height, round, block_hash) && m.step != Step::Prevote) .filter(|m| m.is_aligned(height, round, block_hash) && m.step != Step::Prevote)
.map(|m| m.signature) .map(|m| m.signature.clone());
.collect::<Vec<H520>>() correct_signatures.next().map(|proposal| SealSignatures {
.split_first() proposal: proposal,
votes: correct_signatures.collect()
})
} }
pub fn aligned_votes(&self, message: &ConsensusMessage) -> Vec<&ConsensusMessage> { pub fn aligned_votes(&self, message: &ConsensusMessage) -> Vec<ConsensusMessage> {
self.votes let guard = self.votes.read();
.read() guard.keys()
.keys()
// Get only Propose and Precommits. // Get only Propose and Precommits.
.filter(|m| m.is_aligned(message.height, message.round, message.block_hash) && m.step == message.step) .filter(|m| m.is_aligned(message.height, message.round, message.block_hash) && m.step == message.step)
.cloned()
.collect() .collect()
} }
pub fn aligned_signatures(&self, message: &ConsensusMessage) -> &[H520] { pub fn aligned_signatures(&self, message: &ConsensusMessage) -> Vec<H520> {
self.seal_signatures(message.height, message.round, message.block_hash).map_or(&[], |s| s.1) self.seal_signatures(message.height, message.round, message.block_hash).map_or(Vec::new(), |s| s.votes)
} }
pub fn count_step_votes(&self, height: Height, round: Round, step: Step) -> usize { pub fn count_step_votes(&self, height: Height, round: Round, step: Step) -> usize {