-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add round state machine based on Agnes
- Loading branch information
Showing
7 changed files
with
495 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
max_width = 120 | ||
comment_width = 100 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
} |
Oops, something went wrong.