Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Add a 2/3 quorum option to Authority Round. #10909

Merged
merged 3 commits into from
Aug 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 147 additions & 57 deletions ethcore/src/engines/authority_round/finality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use std::collections::{VecDeque};
use std::collections::hash_map::{HashMap, Entry};

use ethereum_types::{H256, Address};
use types::BlockNumber;

use engines::validator_set::SimpleList;

Expand All @@ -30,21 +31,24 @@ pub struct UnknownValidator;
/// Rolling finality checker for authority round consensus.
/// Stores a chain of unfinalized hashes that can be pushed onto.
pub struct RollingFinality {
headers: VecDeque<(H256, Vec<Address>)>,
headers: VecDeque<(H256, BlockNumber, Vec<Address>)>,
signers: SimpleList,
sign_count: HashMap<Address, usize>,
last_pushed: Option<H256>,
/// First block for which a 2/3 quorum (instead of 1/2) is required.
two_thirds_majority_transition: BlockNumber,
}

impl RollingFinality {
/// Create a blank finality checker under the given validator set.
pub fn blank(signers: Vec<Address>) -> Self {
pub fn blank(signers: Vec<Address>, two_thirds_majority_transition: BlockNumber) -> Self {
trace!(target: "finality", "Instantiating blank RollingFinality with {} signers: {:?}", signers.len(), signers);
RollingFinality {
headers: VecDeque::new(),
signers: SimpleList::new(signers),
sign_count: HashMap::new(),
last_pushed: None,
two_thirds_majority_transition,
}
}

Expand All @@ -53,38 +57,28 @@ impl RollingFinality {
///
/// Fails if any provided signature isn't part of the signers set.
pub fn build_ancestry_subchain<I>(&mut self, iterable: I) -> Result<(), UnknownValidator>
where I: IntoIterator<Item=(H256, Vec<Address>)>
where I: IntoIterator<Item=(H256, BlockNumber, Vec<Address>)>,
{
self.clear();
for (hash, signers) in iterable {
for (hash, number, signers) in iterable {
if signers.iter().any(|s| !self.signers.contains(s)) { return Err(UnknownValidator) }
if self.last_pushed.is_none() { self.last_pushed = Some(hash) }

self.add_signers(&signers);
self.headers.push_front((hash, number, signers));
// break when we've got our first finalized block.
{
let current_signed = self.sign_count.len();

let new_signers = signers.iter().filter(|s| !self.sign_count.contains_key(s)).count();
let would_be_finalized = (current_signed + new_signers) * 2 > self.signers.len();

if would_be_finalized {
trace!(target: "finality", "Encountered already finalized block {}", hash);
break
}

for signer in signers.iter() {
*self.sign_count.entry(*signer).or_insert(0) += 1;
}
if self.is_finalized() {
let (hash, _, signers) = self.headers.pop_front().expect("we just pushed a block; qed");
self.remove_signers(&signers);
trace!(target: "finality", "Encountered already finalized block {}", hash);
break
}

self.headers.push_front((hash, signers));
}

trace!(target: "finality", "Rolling finality state: {:?}", self.headers);
Ok(())
}

/// Clear the finality status, but keeps the validator set.
/// Clears the finality status, but keeps the validator set.
pub fn clear(&mut self) {
self.headers.clear();
self.sign_count.clear();
Expand All @@ -99,7 +93,7 @@ impl RollingFinality {
/// Get an iterator over stored hashes in order.
#[cfg(test)]
pub fn unfinalized_hashes(&self) -> impl Iterator<Item=&H256> {
self.headers.iter().map(|(h, _)| h)
self.headers.iter().map(|(h, _, _)| h)
}

/// Get the validator set.
Expand All @@ -110,97 +104,127 @@ impl RollingFinality {
/// Fails if `signer` isn't a member of the active validator set.
/// Returns a list of all newly finalized headers.
// TODO: optimize with smallvec.
pub fn push_hash(&mut self, head: H256, signers: Vec<Address>) -> Result<Vec<H256>, UnknownValidator> {
pub fn push_hash(&mut self, head: H256, number: BlockNumber, signers: Vec<Address>)
-> Result<Vec<H256>, UnknownValidator>
{
for their_signer in signers.iter() {
if !self.signers.contains(their_signer) {
warn!(target: "finality", "Unknown validator: {}", their_signer);
return Err(UnknownValidator)
}
}

for signer in signers.iter() {
*self.sign_count.entry(*signer).or_insert(0) += 1;
}

self.headers.push_back((head, signers));
self.add_signers(&signers);
self.headers.push_back((head, number, signers));

let mut newly_finalized = Vec::new();

while self.sign_count.len() * 2 > self.signers.len() {
let (hash, signers) = self.headers.pop_front()
while self.is_finalized() {
let (hash, _, signers) = self.headers.pop_front()
.expect("headers length always greater than sign count length; qed");

self.remove_signers(&signers);
newly_finalized.push(hash);

for signer in signers {
match self.sign_count.entry(signer) {
Entry::Occupied(mut entry) => {
// decrement count for this signer and purge on zero.
*entry.get_mut() -= 1;

if *entry.get() == 0 {
entry.remove();
}
}
Entry::Vacant(_) => panic!("all hashes in `header` should have entries in `sign_count` for their signers; qed"),
}
}
}

trace!(target: "finality", "{} Blocks finalized by {:?}: {:?}", newly_finalized.len(), head, newly_finalized);

self.last_pushed = Some(head);
Ok(newly_finalized)
}

/// Returns the first block for which a 2/3 quorum (instead of 1/2) is required.
pub fn two_thirds_majority_transition(&self) -> BlockNumber {
self.two_thirds_majority_transition
}

/// Returns whether the first entry in `self.headers` is finalized.
fn is_finalized(&self) -> bool {
match self.headers.front() {
None => false,
Some((_, number, _)) if *number < self.two_thirds_majority_transition => {
self.sign_count.len() * 2 > self.signers.len()
}
Some((_, _, _)) => {
self.sign_count.len() * 3 > self.signers.len() * 2
}
}
}

/// Adds the signers to the sign count.
fn add_signers(&mut self, signers: &[Address]) {
for signer in signers {
*self.sign_count.entry(*signer).or_insert(0) += 1;
}
}

/// Removes the signers from the sign count.
fn remove_signers(&mut self, signers: &[Address]) {
for signer in signers {
match self.sign_count.entry(*signer) {
Entry::Occupied(mut entry) => {
// decrement count for this signer and purge on zero.
if *entry.get() <= 1 {
entry.remove();
} else {
*entry.get_mut() -= 1;
}
}
Entry::Vacant(_) => {
panic!("all hashes in `header` should have entries in `sign_count` for their signers; qed");
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
}

#[cfg(test)]
mod tests {
use ethereum_types::{H256, Address};
use types::BlockNumber;
use super::RollingFinality;

#[test]
fn rejects_unknown_signers() {
let signers = (0..3).map(|_| Address::random()).collect::<Vec<_>>();
let mut finality = RollingFinality::blank(signers.clone());
assert!(finality.push_hash(H256::random(), vec![signers[0], Address::random()]).is_err());
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
assert!(finality.push_hash(H256::random(), 0, vec![signers[0], Address::random()]).is_err());
}

#[test]
fn finalize_multiple() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();

let mut finality = RollingFinality::blank(signers.clone());
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
let hashes: Vec<_> = (0..7).map(|_| H256::random()).collect();

// 3 / 6 signers is < 51% so no finality.
for (i, hash) in hashes.iter().take(6).cloned().enumerate() {
let i = i % 3;
assert!(finality.push_hash(hash, vec![signers[i]]).unwrap().len() == 0);
assert!(finality.push_hash(hash, i as u64, vec![signers[i]]).unwrap().len() == 0);
}

// after pushing a block signed by a fourth validator, the first four
// blocks of the unverified chain become verified.
assert_eq!(finality.push_hash(hashes[6], vec![signers[4]]).unwrap(),
assert_eq!(finality.push_hash(hashes[6], 6, vec![signers[4]]).unwrap(),
vec![hashes[0], hashes[1], hashes[2], hashes[3]]);
}

#[test]
fn finalize_multiple_signers() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
let mut finality = RollingFinality::blank(signers.clone());
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
let hash = H256::random();

// after pushing a block signed by four validators, it becomes verified right away.
assert_eq!(finality.push_hash(hash, signers[0..4].to_vec()).unwrap(), vec![hash]);
assert_eq!(finality.push_hash(hash, 0, signers[0..4].to_vec()).unwrap(), vec![hash]);
}

#[test]
fn from_ancestry() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
let hashes: Vec<_> = (0..12).map(|i| (H256::random(), vec![signers[i % 6]])).collect();
let hashes: Vec<_> = (0..12).map(|i| (H256::random(), i as u64, vec![signers[i % 6]])).collect();

let mut finality = RollingFinality::blank(signers.clone());
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap();

assert_eq!(finality.unfinalized_hashes().count(), 3);
Expand All @@ -211,15 +235,81 @@ mod tests {
fn from_ancestry_multiple_signers() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
let hashes: Vec<_> = (0..12).map(|i| {
(H256::random(), vec![signers[i % 6], signers[(i + 1) % 6], signers[(i + 2) % 6]])
(H256::random(), i as u64, vec![signers[i % 6], signers[(i + 1) % 6], signers[(i + 2) % 6]])
}).collect();

let mut finality = RollingFinality::blank(signers.clone());
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap();

// only the last hash has < 51% of authorities' signatures
assert_eq!(finality.unfinalized_hashes().count(), 1);
assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0));
assert_eq!(finality.subchain_head(), Some(hashes[11].0));
}

#[test]
fn rejects_unknown_signers_2_3() {
let signers = (0..3).map(|_| Address::random()).collect::<Vec<_>>();
let mut finality = RollingFinality::blank(signers.clone(), 0);
assert!(finality.push_hash(H256::random(), 0, vec![signers[0], Address::random()]).is_err());
}

#[test]
fn finalize_multiple_2_3() {
let signers: Vec<_> = (0..7).map(|_| Address::random()).collect();

let mut finality = RollingFinality::blank(signers.clone(), 0);
let hashes: Vec<_> = (0..9).map(|_| H256::random()).collect();

// 4 / 7 signers is < 67% so no finality.
for (i, hash) in hashes.iter().take(8).cloned().enumerate() {
let i = i % 4;
assert!(finality.push_hash(hash, i as u64, vec![signers[i]]).unwrap().len() == 0);
}

// after pushing a block signed by a fifth validator, the first five
// blocks of the unverified chain become verified.
assert_eq!(finality.push_hash(hashes[8], 8, vec![signers[4]]).unwrap(),
vec![hashes[0], hashes[1], hashes[2], hashes[3], hashes[4]]);
}

#[test]
fn finalize_multiple_signers_2_3() {
let signers: Vec<_> = (0..5).map(|_| Address::random()).collect();
let mut finality = RollingFinality::blank(signers.clone(), 0);
let hash = H256::random();

// after pushing a block signed by four validators, it becomes verified right away.
assert_eq!(finality.push_hash(hash, 0, signers[0..4].to_vec()).unwrap(), vec![hash]);
}

#[test]
fn from_ancestry_2_3() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
let hashes: Vec<_> = (0..12).map(|i| (H256::random(), i as u64, vec![signers[i % 6]])).collect();

let mut finality = RollingFinality::blank(signers, 0);
finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap();

// The last four hashes, with index 11, 10, 9, and 8, have been pushed. 7 would have finalized a block.
assert_eq!(finality.unfinalized_hashes().count(), 4);
assert_eq!(finality.subchain_head(), Some(hashes[11].0));
}

#[test]
fn from_ancestry_multiple_signers_2_3() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
let hashes: Vec<_> = (0..12).map(|i| {
let hash_signers = signers.iter().cycle().skip(i).take(4).cloned().collect();
(H256::random(), i as u64, hash_signers)
}).collect();

let mut finality = RollingFinality::blank(signers.clone(), 0);
finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap();

// only the last hash has < 67% of authorities' signatures
assert_eq!(finality.unfinalized_hashes().count(), 1);
assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0));
assert_eq!(finality.subchain_head(), Some(hashes[11].0));
}
}
Loading