lock rounds
This commit is contained in:
parent
c62795d09b
commit
e90d814193
@ -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() {
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user