Skip to content

Commit

Permalink
Add round state machine based on Agnes
Browse files Browse the repository at this point in the history
  • Loading branch information
romac committed Oct 19, 2023
1 parent f3799fd commit 383d5de
Show file tree
Hide file tree
Showing 7 changed files with 495 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Code/round-sm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "round-sm"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
2 changes: 2 additions & 0 deletions Code/round-sm/rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
max_width = 120
comment_width = 100
18 changes: 18 additions & 0 deletions Code/round-sm/src/events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::{Round, Value};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event {
NewRound, // Start a new round, not as proposer.
NewRoundProposer(Value), // Start a new round and propose the Value.
Proposal(Value, Round), // Receive a proposal with possible polka round.
ProposalInvalid, // Receive an invalid proposal.
PolkaAny, // Receive +2/3 prevotes for anything.
PolkaNil, // Receive +2/3 prevotes for nil.
PolkaValue(Value), // Receive +2/3 prevotes for Value.
PrecommitAny, // Receive +2/3 precommits for anything.
PrecommitValue(Value), // Receive +2/3 precommits for Value.
RoundSkip, // Receive +1/3 votes from a higher round.
TimeoutPropose, // Timeout waiting for proposal.
TimeoutPrevote, // Timeout waiting for prevotes.
TimeoutPrecommit, // Timeout waiting for precommits.
}
119 changes: 119 additions & 0 deletions Code/round-sm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
pub mod events;
pub mod message;
pub mod state;
pub mod state_machine;

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

impl 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 fn new(round: i64) -> Self {
assert!(round >= 0);

Round::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> {
self.as_i64().partial_cmp(&other.as_i64())
}
}

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

impl 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 polka_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,
}
36 changes: 36 additions & 0 deletions Code/round-sm/src/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::{state::RoundValue, Proposal, Round, Timeout, TimeoutStep, Value, Vote};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Message {
NewRound(Round), // Move to the new round.
Proposal(Proposal), // Broadcast the proposal.
Vote(Vote), // Broadcast the vote.
Timeout(Timeout), // Schedule the timeout.
Decision(RoundValue), // Decide the value.
}

impl Message {
pub fn proposal(round: Round, value: Value, polka_round: Round) -> Message {
Message::Proposal(Proposal {
round,
value,
polka_round,
})
}

pub fn prevote(round: Round, value: Option<Value>) -> Message {
Message::Vote(Vote::new_prevote(round, value))
}

pub fn precommit(round: Round, value: Option<Value>) -> Message {
Message::Vote(Vote::new_precommit(round, value))
}

pub fn timeout(round: Round, step: TimeoutStep) -> Message {
Message::Timeout(Timeout { round, step })
}

pub fn decision(round: Round, value: Value) -> Message {
Message::Decision(RoundValue { round, value })
}
}
86 changes: 86 additions & 0 deletions Code/round-sm/src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use crate::{Height, Round, Value};

/// A value and its associated round
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RoundValue {
pub value: Value,
pub round: Round,
}

impl RoundValue {
pub fn new(value: Value, round: Round) -> Self {
Self { value, round }
}
}

/// The step of consensus in this round
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Step {
NewRound,
Propose,
Prevote,
Precommit,
Commit,
}

/// The state of the consensus state machine
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct State {
pub height: Height,
pub round: Round,
pub step: Step,
pub locked: Option<RoundValue>,
pub valid: Option<RoundValue>,
}

impl State {
pub fn new(height: Height) -> Self {
Self {
height,
round: Round::new(0),
step: Step::NewRound,
locked: None,
valid: None,
}
}

pub fn new_round(self, round: Round) -> Self {
Self {
round,
step: Step::NewRound,
..self
}
}

pub fn next_step(self) -> Self {
let step = match self.step {
Step::NewRound => Step::Propose,
Step::Propose => Step::Prevote,
Step::Prevote => Step::Precommit,
_ => self.step,
};

Self { step, ..self }
}

pub fn commit_step(self) -> Self {
Self {
step: Step::Commit,
..self
}
}

pub fn set_locked(self, value: Value) -> Self {
Self {
locked: Some(RoundValue::new(value, self.round)),
..self
}
}

pub fn set_valid(self, value: Value) -> Self {
Self {
valid: Some(RoundValue::new(value, self.round)),
..self
}
}
}
Loading

0 comments on commit 383d5de

Please sign in to comment.