Skip to content

Commit

Permalink
feat: primary implementation of the protocol (#6)
Browse files Browse the repository at this point in the history
* adding structure for ballot

* adding ballot logic

* adding stop method

* fixed party new method

* removing stop, removing separate channel for receiving value

* fixed comments, fixed warnings, removing stop event

* chore: added README.md

* chore: cargo fmt

* chore: add rust ci

* fix: resolve linter problems

* chore: add docs ci

* feat: implement leader election and error handling

* feat: added tests

* chore: cargo fmt

* fix: resolve leader election issues

* feat: added full coverage on update state

* feat: added more tests on party

* feat: computing leader in a randomized manner deterministically using config-based seed

* fix: encapsulate message round state for 2av, 2b and improve event emit error handling

* feat: messages with rkyv, value with bincode

* fix: rename MessageWire to MessagePacket

---------

Co-authored-by: Nikita Masych <[email protected]>
  • Loading branch information
olegfomenko and NikitaMasych authored Aug 15, 2024
1 parent 90d2238 commit 5acf84a
Show file tree
Hide file tree
Showing 14 changed files with 1,369 additions and 254 deletions.
52 changes: 52 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -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 "<meta http-equiv=\"refresh\" content=\"0; url=\">" > 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
135 changes: 135 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -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
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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`.

56 changes: 54 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,56 @@
//! Definition of the BPCon errors.
use std::fmt;

#[derive(Debug)]
pub enum BallotError {
// TODO: define errors.
}
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");
}
}
}
17 changes: 11 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<V: Value> {
/// 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<u64, Option<V>>) -> 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<u64, Option<V>>) -> V;

// TODO: add other fields to update selector state.
}
Loading

0 comments on commit 5acf84a

Please sign in to comment.