Skip to content

Commit

Permalink
Add a consensus executor (#5)
Browse files Browse the repository at this point in the history
* Add skeleton of consensus executor

* Cleanup

* Weight vote by its validators voting power

* Add unit test for `ValidatorSet`

* Split common lib into modules

* WIP: Unit tests for state machine

* Cleanup

* Create new round state machine on NewRound

* Propose checks, id in prevote and precommit (#7)

* Propose checks, id in prevote and precommit

* Disallow `unwrap` and `panic`

* Add proper checks without unwraps

* Add executor test with steps up to precommit

* Formatting and clippy

* Enable more lints

* Finish the happy path test

---------

Co-authored-by: Anca Zamfir <zamfiranca@gmail.com>

---------

Co-authored-by: Anca Zamfir <zamfiranca@gmail.com>
  • Loading branch information
romac and ancazamfir authored Oct 24, 2023
1 parent e4059de commit ccc12e6
Show file tree
Hide file tree
Showing 24 changed files with 1,087 additions and 267 deletions.
1 change: 1 addition & 0 deletions Code/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
name = "malachite-common"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
15 changes: 15 additions & 0 deletions Code/common/src/height.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// TODO: Abstract over Height

/// A blockchain height
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Height(u64);

impl Height {
pub fn new(height: u64) -> Self {
Self(height)
}

pub fn as_u64(&self) -> u64 {
self.0
}
}
186 changes: 27 additions & 159 deletions Code/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,159 +1,27 @@
/// A blockchain height
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Height(u64);

impl Height {
pub fn new(height: u64) -> Self {
Self(height)
}

pub fn as_u64(&self) -> u64 {
self.0
}
}

/// A round number, ie. a natural number
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Round {
/// No round
None,

/// Some round
Some(i64),
}

impl Round {
pub const INITIAL: Round = Round::new(0);

pub const fn new(round: i64) -> Self {
if round < 0 {
Self::None
} else {
Self::Some(round)
}
}

pub fn as_i64(&self) -> i64 {
match self {
Round::None => -1,
Round::Some(r) => *r,
}
}

pub fn is_defined(&self) -> bool {
matches!(self, Round::Some(_))
}

pub fn increment(&self) -> Round {
match self {
Round::None => Round::new(0),
Round::Some(r) => Round::new(r + 1),
}
}
}

impl PartialOrd for Round {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl Ord for Round {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_i64().cmp(&other.as_i64())
}
}

/// The value to decide on
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Value(u64);

impl Value {
pub fn new(value: u64) -> Self {
Self(value)
}

pub fn as_u64(&self) -> u64 {
self.0
}
}

/// A proposal for a value in a round
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Proposal {
pub round: Round,
pub value: Value,
pub pol_round: Round,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum VoteType {
Prevote,
Precommit,
}

/// A vote for a value in a round
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Vote {
pub typ: VoteType,
pub round: Round,
pub value: Option<Value>,
}

impl Vote {
pub fn new_prevote(round: Round, value: Option<Value>) -> Self {
Self {
typ: VoteType::Prevote,
round,
value,
}
}

pub fn new_precommit(round: Round, value: Option<Value>) -> Self {
Self {
typ: VoteType::Precommit,
round,
value,
}
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TimeoutStep {
Propose,
Prevote,
Precommit,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Timeout {
pub round: Round,
pub step: TimeoutStep,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_round() {
// Test Round::new()
assert_eq!(Round::new(-42), Round::None);
assert_eq!(Round::new(-1), Round::None);
assert_eq!(Round::new(0), Round::Some(0));
assert_eq!(Round::new(1), Round::Some(1));
assert_eq!(Round::new(2), Round::Some(2));

// Test Round::as_i64()
assert_eq!(Round::None.as_i64(), -1);
assert_eq!(Round::Some(0).as_i64(), 0);
assert_eq!(Round::Some(1).as_i64(), 1);
assert_eq!(Round::Some(2).as_i64(), 2);

// Test Round::is_defined()
assert!(!Round::None.is_defined());
assert!(Round::Some(0).is_defined());
assert!(Round::Some(1).is_defined());
assert!(Round::Some(2).is_defined());
}
}
//! Common data types and abstractions
#![forbid(unsafe_code)]
#![deny(unused_crate_dependencies, trivial_casts, trivial_numeric_casts)]
#![warn(
// missing_docs,
broken_intra_doc_links,
private_intra_doc_links,
variant_size_differences
)]
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::panic))]

mod height;
mod proposal;
mod round;
mod timeout;
mod validator_set;
mod value;
mod vote;

pub use height::*;
pub use proposal::*;
pub use round::*;
pub use timeout::*;
pub use validator_set::*;
pub use value::*;
pub use vote::*;
21 changes: 21 additions & 0 deletions Code/common/src/proposal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use crate::{Height, Round, Value};

/// A proposal for a value in a round
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Proposal {
pub height: Height,
pub round: Round,
pub value: Value,
pub pol_round: Round,
}

impl Proposal {
pub fn new(height: Height, round: Round, value: Value, pol_round: Round) -> Self {
Self {
height,
round,
value,
pol_round,
}
}
}
89 changes: 89 additions & 0 deletions Code/common/src/round.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/// A round number, ie. a natural number
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Round {
/// No round
None,

/// Some round
Some(i64),
}

impl Round {
pub const INITIAL: Round = Round::new(0);

pub const fn new(round: i64) -> Self {
if round < 0 {
Self::None
} else {
Self::Some(round)
}
}

pub fn as_i64(&self) -> i64 {
match self {
Round::None => -1,
Round::Some(r) => *r,
}
}

pub fn is_defined(&self) -> bool {
matches!(self, Round::Some(r) if *r >= 0)
}

pub fn is_nil(&self) -> bool {
matches!(self, Round::None)
}

pub fn is_valid(&self) -> bool {
match self {
Round::None => true,
Round::Some(r) => *r >= 0,
}
}

pub fn increment(&self) -> Round {
match self {
Round::None => Round::new(0),
Round::Some(r) => Round::new(r + 1),
}
}
}

impl PartialOrd for Round {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl Ord for Round {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_i64().cmp(&other.as_i64())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_round() {
// Test Round::new()
assert_eq!(Round::new(-42), Round::None);
assert_eq!(Round::new(-1), Round::None);
assert_eq!(Round::new(0), Round::Some(0));
assert_eq!(Round::new(1), Round::Some(1));
assert_eq!(Round::new(2), Round::Some(2));

// Test Round::as_i64()
assert_eq!(Round::None.as_i64(), -1);
assert_eq!(Round::Some(0).as_i64(), 0);
assert_eq!(Round::Some(1).as_i64(), 1);
assert_eq!(Round::Some(2).as_i64(), 2);

// Test Round::is_defined()
assert!(!Round::None.is_defined());
assert!(Round::Some(0).is_defined());
assert!(Round::Some(1).is_defined());
assert!(Round::Some(2).is_defined());
}
}
14 changes: 14 additions & 0 deletions Code/common/src/timeout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crate::Round;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TimeoutStep {
Propose,
Prevote,
Precommit,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Timeout {
pub round: Round,
pub step: TimeoutStep,
}
Loading

0 comments on commit ccc12e6

Please sign in to comment.