diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..b7b02e4
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,52 @@
+name: Deploy Rust Docs to GitHub Pages
+
+on:
+ push:
+ branches:
+ - main
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout sources
+ uses: actions/checkout@v4
+
+ - name: Install stable toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+ override: true
+
+ - name: Build Rust Documentation
+ run: |
+ cargo doc --no-deps --document-private-items
+ echo "" > target/doc/index.html
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./target/doc
+
+ deploy:
+ runs-on: ubuntu-latest
+ needs: build
+
+ permissions:
+ pages: write
+ id-token: write
+
+ environment:
+ name: docs
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
new file mode 100644
index 0000000..f8d9382
--- /dev/null
+++ b/.github/workflows/rust.yml
@@ -0,0 +1,135 @@
+name: Rust CI
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+jobs:
+ check:
+ name: Check
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout sources
+ uses: actions/checkout@v4
+
+ - name: Cache Cargo registry
+ uses: actions/cache@v4
+ with:
+ path: ~/.cargo/registry
+ key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-registry-
+
+ - name: Cache Cargo build
+ uses: actions/cache@v4
+ with:
+ path: target
+ key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-build-
+
+ - name: Install stable toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+ override: true
+
+ - name: Run cargo check
+ uses: actions-rs/cargo@v1
+ continue-on-error: false
+ with:
+ command: check
+
+ test:
+ name: Test Suite
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ rust: [stable, beta, nightly]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: Checkout sources
+ uses: actions/checkout@v4
+
+ - name: Cache Cargo registry
+ uses: actions/cache@v4
+ with:
+ path: ~/.cargo/registry
+ key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-registry-
+
+ - name: Cache Cargo build
+ uses: actions/cache@v4
+ with:
+ path: target
+ key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-build-
+
+ - name: Install ${{ matrix.rust }} toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: ${{ matrix.rust }}
+
+ - name: Run cargo test
+ uses: actions-rs/cargo@v1
+ continue-on-error: false
+ with:
+ command: test
+ args: --all-features --verbose
+
+ lints:
+ name: Lints
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout sources
+ uses: actions/checkout@v4
+
+ - name: Cache Cargo registry
+ uses: actions/cache@v4
+ with:
+ path: ~/.cargo/registry
+ key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-registry-
+
+ - name: Cache Cargo build
+ uses: actions/cache@v4
+ with:
+ path: target
+ key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-cargo-build-
+
+ - name: Install stable toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+ override: true
+ components: rustfmt, clippy
+
+ - name: Run cargo fmt
+ uses: actions-rs/cargo@v1
+ continue-on-error: false
+ with:
+ command: fmt
+ args: --all -- --check
+
+ - name: Run cargo clippy
+ uses: actions-rs/cargo@v1
+ continue-on-error: false
+ with:
+ command: clippy
+ args: --all-targets -- -D warnings
diff --git a/Cargo.toml b/Cargo.toml
index 287eb66..4383852 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,4 +6,8 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-serde = { version = "1.0.204", features = ["derive"] }
+rand = "0.9.0-alpha.2"
+log = "0.4.22"
+rkyv = { version = "0.7.44", features = ["validation"]}
+serde = { version = "1.0.207", features = ["derive"] }
+bincode = "1.3.3"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6d8f44a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,27 @@
+# BPCon Rust Library
+
+[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
+
+This is a generic rust implementation of the `BPCon` consensus mechanism.
+
+## Library Structure
+
+### src/party.rs
+
+Main entity in this implementation is `Party` - it represents member of the consensus.
+
+External system shall create desired amount of parties.
+
+We have 2 communication channels - one for sending `MessageWire` - encoded in bytes message and routing information,
+and the other for pitching consensus events - this allows for external system to impose custom limitations and rules
+regarding runway.
+
+### src/message.rs
+
+Definitions of the general message struct, routing information and type-specific contents.
+
+### src/lib.rs
+
+Here we present a trait for the value on which consensus is being conducted. Additionally, there is a trait for
+defining custom value selection rules, called `ValueSelector`.
+
diff --git a/src/error.rs b/src/error.rs
index 032fdc9..b9d84b8 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,4 +1,56 @@
//! Definition of the BPCon errors.
+
+use std::fmt;
+
+#[derive(Debug)]
pub enum BallotError {
- // TODO: define errors.
-}
\ No newline at end of file
+ MessageParsing(String),
+ ValueParsing(String),
+ InvalidState(String),
+ Communication(String),
+ LeaderElection(String),
+}
+
+impl fmt::Display for BallotError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ BallotError::MessageParsing(ref err) => write!(f, "Message parsing error: {}", err),
+ BallotError::ValueParsing(ref err) => write!(f, "Value parsing error: {}", err),
+ BallotError::InvalidState(ref err) => write!(f, "Invalid state error: {}", err),
+ BallotError::Communication(ref err) => write!(f, "Communication error: {}", err),
+ BallotError::LeaderElection(ref err) => write!(f, "Leader election error: {}", err),
+ }
+ }
+}
+
+impl std::error::Error for BallotError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ // Since these are all simple String errors, there is no underlying source error.
+ None
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_ballot_error_message_parsing() {
+ let error = BallotError::MessageParsing("Parsing failed".into());
+ if let BallotError::MessageParsing(msg) = error {
+ assert_eq!(msg, "Parsing failed");
+ } else {
+ panic!("Expected MessageParsing error");
+ }
+ }
+
+ #[test]
+ fn test_ballot_error_invalid_state() {
+ let error = BallotError::InvalidState("Invalid state transition".into());
+ if let BallotError::InvalidState(msg) = error {
+ assert_eq!(msg, "Invalid state transition");
+ } else {
+ panic!("Expected InvalidState error");
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index b8fa6a5..afa03b0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,18 +1,23 @@
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+mod error;
pub mod message;
pub mod party;
-mod error;
/// General trait for value itself.
-pub trait Value: Eq {}
+pub trait Value: Eq + Serialize + for<'a> Deserialize<'a> + Clone {}
/// Trait for value selector and verificator.
/// Value selection and verification may depend on different conditions for different values.
+/// Note that value selection should follow the rules of BPCon: only safe values can be selected.
+/// Party can not vote for different values, even in different ballots.
pub trait ValueSelector {
- /// Verifies if a value is selected correctly.
- fn verify(v: V) -> bool;
+ /// Verifies if a value is selected correctly. Accepts 2b messages from parties.
+ fn verify(&self, v: &V, m: &HashMap>) -> bool;
- /// Select value depending on inner conditions.
- fn select() -> V;
+ /// Select value depending on inner conditions. Accepts 2b messages from parties.
+ fn select(&self, m: &HashMap>) -> V;
// TODO: add other fields to update selector state.
}
diff --git a/src/message.rs b/src/message.rs
index eb7c68e..daa86ab 100644
--- a/src/message.rs
+++ b/src/message.rs
@@ -1,32 +1,11 @@
-//! Definition of the BPCon message trait and enum.
+//! Definition of the BPCon messages.
-pub mod msg1a;
-pub mod msg1b;
-pub mod msg1c;
-pub mod msg2a;
-pub mod msg2av;
-pub mod msg2b;
-
-use serde::{Deserialize, Serialize};
-
-/// Generic communicative unit in ballot.
-pub trait Message: Serialize + for<'a> Deserialize<'a> {
- /// Which participant this message came from.
- fn get_sender_id(&self) -> u64;
- /// Where this message should be delivered.
- fn get_receivers_id(&self) -> Vec;
- /// Indicates whether this message shall be broadcast to other participants. Can be empty if `is_broadcast` is `true`
- fn is_broadcast(&self) -> bool;
- /// Encode inner message to bytes and receive routing information.
- fn msg_routing(&self) -> MessageRouting;
- /// Returns the BPCon message type.
- fn msg_type(&self) -> ProtocolMessage;
-}
+use rkyv::{AlignedVec, Archive, Deserialize, Serialize};
/// Message ready for transfer.
-pub struct MessageWire{
+pub struct MessagePacket {
/// Serialized message contents.
- pub content_bytes: Vec,
+ pub content_bytes: AlignedVec,
/// Routing information.
pub routing: MessageRouting,
}
@@ -47,8 +26,104 @@ pub struct MessageRouting {
pub enum ProtocolMessage {
Msg1a,
Msg1b,
- Msg1c,
Msg2a,
Msg2av,
Msg2b,
-}
\ No newline at end of file
+}
+
+// Value in messages is stored in serialized format, i.e bytes in order to omit
+// strict restriction for `Value` trait to be [de]serializable only with `rkyv`.
+
+#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
+#[archive(compare(PartialEq), check_bytes)]
+#[archive_attr(derive(Debug))]
+pub struct Message1aContent {
+ pub ballot: u64,
+}
+
+#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
+#[archive(compare(PartialEq), check_bytes)]
+#[archive_attr(derive(Debug))]
+pub struct Message1bContent {
+ pub ballot: u64,
+ pub last_ballot_voted: Option,
+ pub last_value_voted: Option>,
+}
+
+#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
+#[archive(compare(PartialEq), check_bytes)]
+#[archive_attr(derive(Debug))]
+pub struct Message2aContent {
+ pub ballot: u64,
+ pub value: Vec,
+}
+
+#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
+#[archive(compare(PartialEq), check_bytes)]
+#[archive_attr(derive(Debug))]
+pub struct Message2avContent {
+ pub ballot: u64,
+ pub received_value: Vec,
+}
+
+#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
+#[archive(compare(PartialEq), check_bytes)]
+#[archive_attr(derive(Debug))]
+pub struct Message2bContent {
+ pub ballot: u64,
+}
+
+impl Message1aContent {
+ pub fn get_routing(id: u64) -> MessageRouting {
+ MessageRouting {
+ sender: id,
+ receivers: vec![],
+ is_broadcast: true,
+ msg_type: ProtocolMessage::Msg1a,
+ }
+ }
+}
+
+impl Message1bContent {
+ pub fn get_routing(id: u64) -> MessageRouting {
+ MessageRouting {
+ sender: id,
+ receivers: vec![],
+ is_broadcast: true,
+ msg_type: ProtocolMessage::Msg1b,
+ }
+ }
+}
+
+impl Message2aContent {
+ pub fn get_routing(id: u64) -> MessageRouting {
+ MessageRouting {
+ sender: id,
+ receivers: vec![],
+ is_broadcast: true,
+ msg_type: ProtocolMessage::Msg2a,
+ }
+ }
+}
+
+impl Message2avContent {
+ pub fn get_routing(id: u64) -> MessageRouting {
+ MessageRouting {
+ sender: id,
+ receivers: vec![],
+ is_broadcast: true,
+ msg_type: ProtocolMessage::Msg2av,
+ }
+ }
+}
+
+impl Message2bContent {
+ pub fn get_routing(id: u64) -> MessageRouting {
+ MessageRouting {
+ sender: id,
+ receivers: vec![],
+ is_broadcast: true,
+ msg_type: ProtocolMessage::Msg2b,
+ }
+ }
+}
diff --git a/src/message/msg1a.rs b/src/message/msg1a.rs
deleted file mode 100644
index cb9ad9f..0000000
--- a/src/message/msg1a.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//! Definition of the BPCon messages implementation.
-
-use serde::{Deserialize, Serialize};
-use crate::message::{Message, MessageRouting, ProtocolMessage};
-
-#[derive(Serialize, Deserialize, Clone, Debug)]
-pub struct Message1a {}
-
-impl Message for Message1a {
- fn get_sender_id(&self) -> u64 {
- todo!()
- }
-
- fn get_receivers_id(&self) -> Vec {
- todo!()
- }
-
- fn is_broadcast(&self) -> bool {
- todo!()
- }
-
- fn msg_routing(&self) -> MessageRouting {
- todo!()
- }
-
- fn msg_type(&self) -> ProtocolMessage {
- todo!()
- }
-}
diff --git a/src/message/msg1b.rs b/src/message/msg1b.rs
deleted file mode 100644
index 8e9ab23..0000000
--- a/src/message/msg1b.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//! Definition of the BPCon messages implementation.
-
-use serde::{Deserialize, Serialize};
-use crate::message::{Message, MessageRouting, ProtocolMessage};
-
-#[derive(Serialize, Deserialize, Clone, Debug)]
-pub struct Message1b {}
-
-impl Message for Message1b {
- fn get_sender_id(&self) -> u64 {
- todo!()
- }
-
- fn get_receivers_id(&self) -> Vec {
- todo!()
- }
-
- fn is_broadcast(&self) -> bool {
- todo!()
- }
-
- fn msg_routing(&self) -> MessageRouting {
- todo!()
- }
-
- fn msg_type(&self) -> ProtocolMessage {
- todo!()
- }
-}
diff --git a/src/message/msg1c.rs b/src/message/msg1c.rs
deleted file mode 100644
index bb91682..0000000
--- a/src/message/msg1c.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//! Definition of the BPCon messages implementation.
-
-use serde::{Deserialize, Serialize};
-use crate::message::{Message, MessageRouting, ProtocolMessage};
-
-#[derive(Serialize, Deserialize, Clone, Debug)]
-pub struct Message1c {}
-
-impl Message for Message1c {
- fn get_sender_id(&self) -> u64 {
- todo!()
- }
-
- fn get_receivers_id(&self) -> Vec {
- todo!()
- }
-
- fn is_broadcast(&self) -> bool {
- todo!()
- }
-
- fn msg_routing(&self) -> MessageRouting {
- todo!()
- }
-
- fn msg_type(&self) -> ProtocolMessage {
- todo!()
- }
-}
diff --git a/src/message/msg2a.rs b/src/message/msg2a.rs
deleted file mode 100644
index 9a97d74..0000000
--- a/src/message/msg2a.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//! Definition of the BPCon messages implementation.
-
-use serde::{Deserialize, Serialize};
-use crate::message::{Message, MessageRouting, ProtocolMessage};
-
-#[derive(Serialize, Deserialize, Clone, Debug)]
-pub struct Message2a {}
-
-impl Message for Message2a {
- fn get_sender_id(&self) -> u64 {
- todo!()
- }
-
- fn get_receivers_id(&self) -> Vec {
- todo!()
- }
-
- fn is_broadcast(&self) -> bool {
- todo!()
- }
-
- fn msg_routing(&self) -> MessageRouting {
- todo!()
- }
-
- fn msg_type(&self) -> ProtocolMessage {
- todo!()
- }
-}
diff --git a/src/message/msg2av.rs b/src/message/msg2av.rs
deleted file mode 100644
index bd0c5a8..0000000
--- a/src/message/msg2av.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//! Definition of the BPCon messages implementation.
-
-use serde::{Deserialize, Serialize};
-use crate::message::{Message, MessageRouting, ProtocolMessage};
-
-#[derive(Serialize, Deserialize, Clone, Debug)]
-pub struct Message2av {}
-
-impl Message for Message2av {
- fn get_sender_id(&self) -> u64 {
- todo!()
- }
-
- fn get_receivers_id(&self) -> Vec {
- todo!()
- }
-
- fn is_broadcast(&self) -> bool {
- todo!()
- }
-
- fn msg_routing(&self) -> MessageRouting {
- todo!()
- }
-
- fn msg_type(&self) -> ProtocolMessage {
- todo!()
- }
-}
diff --git a/src/message/msg2b.rs b/src/message/msg2b.rs
deleted file mode 100644
index 0c3f9b5..0000000
--- a/src/message/msg2b.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//! Definition of the BPCon messages implementation.
-
-use serde::{Deserialize, Serialize};
-use crate::message::{Message, MessageRouting, ProtocolMessage};
-
-#[derive(Serialize, Deserialize, Clone, Debug)]
-pub struct Message2b {}
-
-impl Message for Message2b {
- fn get_sender_id(&self) -> u64 {
- todo!()
- }
-
- fn get_receivers_id(&self) -> Vec {
- todo!()
- }
-
- fn is_broadcast(&self) -> bool {
- todo!()
- }
-
- fn msg_routing(&self) -> MessageRouting {
- todo!()
- }
-
- fn msg_type(&self) -> ProtocolMessage {
- todo!()
- }
-}
diff --git a/src/party.rs b/src/party.rs
index 4be1131..85f6b6f 100644
--- a/src/party.rs
+++ b/src/party.rs
@@ -1,13 +1,145 @@
//! Definition of the BPCon participant structure.
-use std::sync::mpsc::{Receiver, Sender};
-use crate::{Value, ValueSelector};
use crate::error::BallotError;
-use crate::message::{MessageRouting, MessageWire, ProtocolMessage};
+use crate::message::{
+ Message1aContent, Message1bContent, Message2aContent, Message2avContent, Message2bContent,
+ MessagePacket, MessageRouting, ProtocolMessage,
+};
+use crate::{Value, ValueSelector};
+use rand::prelude::StdRng;
+use rand::prelude::*;
+use rand::Rng;
+use rkyv::{AlignedVec, Deserialize, Infallible};
+use std::cmp::PartialEq;
+use std::collections::hash_map::DefaultHasher;
+use std::collections::hash_map::Entry::Vacant;
+use std::collections::{HashMap, HashSet};
+use std::hash::{Hash, Hasher};
+use std::sync::mpsc::{channel, Receiver, Sender};
+
+/// BPCon configuration. Includes ballot time bounds and other stuff.
+pub struct BPConConfig {
+ /// Parties weights: `party_weights[i]` corresponds to the i-th party weight
+ pub party_weights: Vec,
+
+ /// Threshold weight to define BFT quorum: should be > 2/3 of total weight
+ pub threshold: u128,
+
+ /// Leader of the ballot, computed using seed obtained from config.
+ leader: u64,
+ // TODO: define other config fields.
+}
+
+impl BPConConfig {
+ /// Create new config instance.
+ pub fn new(party_weights: Vec, threshold: u128) -> Self {
+ let mut cfg = Self {
+ party_weights,
+ threshold,
+ leader: 0,
+ };
+ cfg.leader = cfg.compute_leader().unwrap();
+
+ cfg
+ }
+
+ /// Compute leader in a weighed randomized manner.
+ /// Uses seed from the config, making it deterministic.
+ fn compute_leader(&self) -> Result {
+ let seed = self.compute_seed();
+
+ let total_weight: u64 = self.party_weights.iter().sum();
+ if total_weight == 0 {
+ return Err(BallotError::LeaderElection("Zero weight sum".into()));
+ }
+
+ // Use the seed from the config to create a deterministic random number generator.
+ let mut rng = StdRng::seed_from_u64(seed);
+
+ let random_value: u64 = rng.gen_range(0..total_weight);
+
+ let mut cumulative_weight = 0;
+ for (i, &weight) in self.party_weights.iter().enumerate() {
+ cumulative_weight += weight;
+ if random_value < cumulative_weight {
+ return Ok(i as u64);
+ }
+ }
+ Err(BallotError::LeaderElection("Election failed".into()))
+ }
+
+ /// Compute seed for randomized leader election.
+ fn compute_seed(&self) -> u64 {
+ let mut hasher = DefaultHasher::new();
+
+ // Hash each field that should contribute to the seed
+ self.party_weights.hash(&mut hasher);
+ self.threshold.hash(&mut hasher);
+
+ // You can add more fields as needed
+
+ // Generate the seed from the hash
+ hasher.finish()
+ }
+}
-/// BPCon configuration. Includes ballot time bounds, and other stuff.
-pub struct BallotConfig {
- // TODO: define config fields.
+/// Party status defines the statuses of the ballot for the particular participant
+/// depending on local calculations.
+#[derive(PartialEq, Debug)]
+pub(crate) enum PartyStatus {
+ None,
+ Launched,
+ Passed1a,
+ Passed1b,
+ Passed2a,
+ Passed2av,
+ Passed2b,
+ Finished,
+ Failed,
+}
+
+/// Party events is used for the ballot flow control.
+#[derive(PartialEq)]
+pub(crate) enum PartyEvent {
+ Launch1a,
+ Launch1b,
+ Launch2a,
+ Launch2av,
+ Launch2b,
+ Finalize,
+}
+
+/// A struct to keep track of senders and the cumulative weight of their messages.
+struct MessageRoundState {
+ senders: HashSet,
+ weight: u128,
+}
+
+impl MessageRoundState {
+ /// Creates a new instance of `MessageRoundState`.
+ fn new() -> Self {
+ Self {
+ senders: HashSet::new(),
+ weight: 0,
+ }
+ }
+
+ /// Adds a sender and their corresponding weight.
+ fn add_sender(&mut self, sender: u64, weight: u128) {
+ self.senders.insert(sender);
+ self.weight += weight;
+ }
+
+ /// Checks if the sender has already sent a message.
+ fn contains_sender(&self, sender: &u64) -> bool {
+ self.senders.contains(sender)
+ }
+
+ /// Resets the state.
+ fn reset(&mut self) {
+ self.senders.clear();
+ self.weight = 0;
+ }
}
/// Party of the BPCon protocol that executes ballot.
@@ -24,67 +156,874 @@ pub struct BallotConfig {
pub struct Party> {
/// This party's identifier.
pub id: u64,
- /// Other ballot parties' ids.
- pub party_ids: Vec,
/// Communication queues.
- in_receiver: Receiver,
- out_sender: Sender,
+ msg_in_receiver: Receiver,
+ msg_out_sender: Sender,
- /// Query to submit result.
- value_sender: Sender>,
+ /// Query to receive and send events that run ballot protocol
+ event_receiver: Receiver,
+ event_sender: Sender,
- /// Ballot config (e.g. ballot time bounds).
- cfg: BallotConfig,
+ /// BPCon config (e.g. ballot time bounds, parties weights, etc.).
+ cfg: BPConConfig,
/// Main functional for value selection.
value_selector: VS,
- // TODO: define other state fields if needed.
+ /// Status of the ballot execution
+ status: PartyStatus,
+
+ /// Current ballot number
+ ballot: u64,
+
+ /// Last ballot where party submitted 2b message
+ last_ballot_voted: Option,
+
+ /// Last value for which party submitted 2b message
+ last_value_voted: Option,
+
+ /// Local round fields
+
+ /// 1b round state
+ ///
+ parties_voted_before: HashMap>, // id <-> value
+ messages_1b_weight: u128,
+
+ /// 2a round state
+ ///
+ value_2a: Option,
+
+ /// 2av round state
+ ///
+ messages_2av_state: MessageRoundState,
+
+ /// 2b round state
+ ///
+ messages_2b_state: MessageRoundState,
}
impl> Party {
pub fn new(
id: u64,
- party_ids: Vec,
- in_receiver: Receiver,
- out_sender: Sender,
- value_sender: Sender>,
- cfg: BallotConfig,
+ cfg: BPConConfig,
value_selector: VS,
- ) -> Self {
- Self {
- id,
- party_ids,
- in_receiver,
- out_sender,
- value_sender,
- cfg,
- value_selector,
+ ) -> (Self, Receiver, Sender) {
+ let (event_sender, event_receiver) = channel();
+ let (msg_in_sender, msg_in_receiver) = channel();
+ let (msg_out_sender, msg_out_receiver) = channel();
+
+ (
+ Self {
+ id,
+ msg_in_receiver,
+ msg_out_sender,
+ event_receiver,
+ event_sender,
+ cfg,
+ value_selector,
+ status: PartyStatus::None,
+ ballot: 0,
+ last_ballot_voted: None,
+ last_value_voted: None,
+ parties_voted_before: HashMap::new(),
+ messages_1b_weight: 0,
+ value_2a: None,
+ messages_2av_state: MessageRoundState::new(),
+ messages_2b_state: MessageRoundState::new(),
+ },
+ msg_out_receiver,
+ msg_in_sender,
+ )
+ }
+
+ pub fn ballot(&self) -> u64 {
+ self.ballot
+ }
+
+ pub fn is_launched(&self) -> bool {
+ !self.is_stopped()
+ }
+
+ pub fn is_stopped(&self) -> bool {
+ self.status == PartyStatus::Finished || self.status == PartyStatus::Failed
+ }
+
+ pub fn get_value_selected(&self) -> Option {
+ // Only `Finished` status means reached BFT agreement
+ if self.status == PartyStatus::Finished {
+ return self.value_2a.clone();
+ }
+
+ None
+ }
+
+ fn get_value(&self) -> V {
+ self.value_selector.select(&self.parties_voted_before)
+ }
+
+ /// Start the next ballot. It's expected from the external system to re-run ballot protocol in
+ /// case of failed ballot.
+ pub async fn launch_ballot(&mut self) -> Result