Skip to main content

hashgraph_like_consensus/
lib.rs

1//! A lightweight consensus library for making binary decisions in P2P networks.
2//!
3//! This library implements the [Hashgraph-like Consensus Protocol](https://github.com/vacp2p/rfc-index/blob/main/vac/raw/consensus-hashgraphlike.md),
4//! which lets groups of peers vote on proposals and reach agreement even when some peers are
5//! unreliable or malicious. It's designed to be fast (O(log n) rounds), secure (Byzantine fault
6//! tolerant), and easy to embed in your application.
7//!
8//! # How it works
9//!
10//! 1. Someone creates a [`types::CreateProposalRequest`] within a [`scope`].
11//! 2. Peers cast votes (yes/no), each cryptographically signed and linked into a hashgraph.
12//! 3. Once enough votes are collected the session transitions to
13//!    [`session::ConsensusState::ConsensusReached`] and a [`types::ConsensusEvent`] is emitted.
14//!
15//! # What the library does NOT do
16//!
17//! This library handles **consensus calculation** (vote validation, hashgraph
18//! verification, threshold math, liveness rules). It performs **no I/O and no
19//! orchestration**. Your application is responsible for:
20//!
21//! - **Network propagation** — gossip proposals and votes to peers yourself;
22//!   call [`process_incoming_proposal`](service::ConsensusService::process_incoming_proposal)
23//!   / [`process_incoming_vote`](service::ConsensusService::process_incoming_vote) on receipt.
24//! - **Timeout scheduling** — schedule a timer per proposal and call
25//!   [`handle_consensus_timeout`](service::ConsensusService::handle_consensus_timeout)
26//!   when it fires. Without this, proposals with offline voters stay `Active`
27//!   forever and silent-peer liveness logic never runs.
28//! - **`expected_voters_count` accuracy** — this drives all threshold math;
29//!   a wrong value produces wrong results.
30//! - **Session eviction awareness** — the default service keeps at most 10
31//!   sessions per scope; older sessions are silently dropped.
32//!
33//! # Architecture
34//!
35//! [`ConsensusService`](service::ConsensusService) is the single entry point.
36//! It is generic over two pluggable backends:
37//!
38//! - [`ConsensusStorage`](storage::ConsensusStorage) — where sessions and votes
39//!   are persisted (in-memory, database, etc.)
40//! - [`ConsensusEventBus`](events::ConsensusEventBus) — how consensus events
41//!   are delivered (broadcast channel, message queue, etc.)
42//!
43//! All consensus business logic lives in the service. The backends are pure
44//! infrastructure — swap them without changing any consensus behavior.
45//! Use [`storage()`](service::ConsensusService::storage) and
46//! [`event_bus()`](service::ConsensusService::event_bus) to access the backends
47//! directly for reads, cleanup, and event subscription.
48//!
49//! # Getting started
50//!
51//! The main entry point is [`service::DefaultConsensusService`] (a type alias for
52//! [`service::ConsensusService`] with in-memory storage and broadcast events).
53//!
54//! ```rust,no_run
55//! use hashgraph_like_consensus::{
56//!     scope::ScopeID,
57//!     service::DefaultConsensusService,
58//!     signing::{ConsensusSignatureScheme, EthereumConsensusSigner},
59//!     types::CreateProposalRequest,
60//! };
61//! use alloy::signers::local::PrivateKeySigner;
62//!
63//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
64//! let service = DefaultConsensusService::default();
65//! let scope = ScopeID::from("my-scope");
66//! let signer = EthereumConsensusSigner::new(PrivateKeySigner::random());
67//!
68//! let proposal = service
69//!     .create_proposal(
70//!         &scope,
71//!         CreateProposalRequest::new(
72//!             "Upgrade contract".into(),
73//!             b"Switch to v2".to_vec(),
74//!             signer.identity().to_vec(),
75//!             3,    // expected voters
76//!             60,   // expiration (seconds from now)
77//!             true, // liveness: silent peers count as YES at timeout
78//!         )?,
79//!     )
80//!     .await?;
81//!
82//! let vote = service
83//!     .cast_vote(&scope, proposal.proposal_id, true, signer)
84//!     .await?;
85//! # Ok(())
86//! # }
87//! ```
88//!
89//! # Modules
90//!
91//! | Module | Purpose |
92//! |--------|---------|
93//! | [`service`] | [`ConsensusService`](service::ConsensusService) and [`DefaultConsensusService`](service::DefaultConsensusService) |
94//! | [`session`] | [`ConsensusSession`](session::ConsensusSession), [`ConsensusConfig`](session::ConsensusConfig), and [`ConsensusState`](session::ConsensusState) |
95//! | [`scope`] | [`ConsensusScope`](scope::ConsensusScope) trait and [`ScopeID`](scope::ScopeID) alias |
96//! | [`scope_config`] | Per-scope defaults ([`ScopeConfig`](scope_config::ScopeConfig), [`NetworkType`](scope_config::NetworkType)) |
97//! | [`types`] | Request/event types ([`CreateProposalRequest`](types::CreateProposalRequest), [`ConsensusEvent`](types::ConsensusEvent)) |
98//! | [`storage`] | [`ConsensusStorage`](storage::ConsensusStorage) trait and [`InMemoryConsensusStorage`](storage::InMemoryConsensusStorage) |
99//! | [`events`] | [`ConsensusEventBus`](events::ConsensusEventBus) trait and [`BroadcastEventBus`](events::BroadcastEventBus) |
100//! | [`signing`] | [`ConsensusSignatureScheme`](signing::ConsensusSignatureScheme) trait and the default [`EthereumConsensusSigner`](signing::EthereumConsensusSigner) impl |
101//! | [`error`] | [`ConsensusError`](error::ConsensusError) enum |
102//! | [`utils`] | Low-level validation and hashing helpers |
103
104pub mod protos {
105    pub mod consensus {
106        pub mod v1 {
107            include!(concat!(env!("OUT_DIR"), "/consensus.v1.rs"));
108        }
109    }
110}
111
112pub mod error;
113pub mod events;
114pub mod scope;
115pub mod scope_config;
116pub mod service;
117pub mod service_stats;
118pub mod session;
119pub mod signing;
120pub mod storage;
121pub mod types;
122pub mod utils;