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//! types::CreateProposalRequest,
59//! };
60//! use alloy::signers::local::PrivateKeySigner;
61//!
62//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
63//! let service = DefaultConsensusService::default();
64//! let scope = ScopeID::from("my-scope");
65//! let signer = PrivateKeySigner::random();
66//!
67//! let proposal = service
68//! .create_proposal(
69//! &scope,
70//! CreateProposalRequest::new(
71//! "Upgrade contract".into(),
72//! b"Switch to v2".to_vec(),
73//! signer.address().as_slice().to_vec(),
74//! 3, // expected voters
75//! 60, // expiration (seconds from now)
76//! true, // liveness: silent peers count as YES at timeout
77//! )?,
78//! )
79//! .await?;
80//!
81//! let vote = service
82//! .cast_vote(&scope, proposal.proposal_id, true, signer)
83//! .await?;
84//! # Ok(())
85//! # }
86//! ```
87//!
88//! # Modules
89//!
90//! | Module | Purpose |
91//! |--------|---------|
92//! | [`service`] | [`ConsensusService`](service::ConsensusService) and [`DefaultConsensusService`](service::DefaultConsensusService) |
93//! | [`session`] | [`ConsensusSession`](session::ConsensusSession), [`ConsensusConfig`](session::ConsensusConfig), and [`ConsensusState`](session::ConsensusState) |
94//! | [`scope`] | [`ConsensusScope`](scope::ConsensusScope) trait and [`ScopeID`](scope::ScopeID) alias |
95//! | [`scope_config`] | Per-scope defaults ([`ScopeConfig`](scope_config::ScopeConfig), [`NetworkType`](scope_config::NetworkType)) |
96//! | [`types`] | Request/event types ([`CreateProposalRequest`](types::CreateProposalRequest), [`ConsensusEvent`](types::ConsensusEvent)) |
97//! | [`storage`] | [`ConsensusStorage`](storage::ConsensusStorage) trait and [`InMemoryConsensusStorage`](storage::InMemoryConsensusStorage) |
98//! | [`events`] | [`ConsensusEventBus`](events::ConsensusEventBus) trait and [`BroadcastEventBus`](events::BroadcastEventBus) |
99//! | [`error`] | [`ConsensusError`](error::ConsensusError) enum |
100//! | [`utils`] | Low-level validation and hashing helpers |
101
102pub mod protos {
103 pub mod consensus {
104 pub mod v1 {
105 include!(concat!(env!("OUT_DIR"), "/consensus.v1.rs"));
106 }
107 }
108}
109
110pub mod error;
111pub mod events;
112pub mod scope;
113pub mod scope_config;
114pub mod service;
115pub mod service_stats;
116pub mod session;
117pub mod storage;
118pub mod types;
119pub mod utils;