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