pub struct ConsensusService<Scope, S, E>{ /* private fields */ }Expand description
The main service that handles proposals, votes, and consensus.
This is the main entry point for using the consensus service. It handles creating proposals, processing votes, and managing timeouts.
Implementations§
Source§impl ConsensusService<String, InMemoryConsensusStorage<String>, BroadcastEventBus<String>>
impl ConsensusService<String, InMemoryConsensusStorage<String>, BroadcastEventBus<String>>
Sourcepub fn new_with_max_sessions(max_sessions_per_scope: usize) -> Self
pub fn new_with_max_sessions(max_sessions_per_scope: usize) -> Self
Create a service with a custom limit on how many sessions can exist per scope.
When the limit is reached, older sessions are automatically removed to make room. Eviction is silent — no event is emitted. Archive results you need before they are evicted.
Source§impl<Scope, S, E> ConsensusService<Scope, S, E>
impl<Scope, S, E> ConsensusService<Scope, S, E>
Sourcepub fn new_with_components(
storage: S,
event_bus: E,
max_sessions_per_scope: usize,
) -> Self
pub fn new_with_components( storage: S, event_bus: E, max_sessions_per_scope: usize, ) -> Self
Build a service with your own storage and event bus implementations.
Use this when you need custom persistence (like a database) or event handling.
The max_sessions_per_scope parameter controls how many sessions can exist per scope.
When the limit is reached, older sessions are automatically removed.
Sourcepub fn storage(&self) -> &S
pub fn storage(&self) -> &S
Access the underlying storage backend.
Use this for reading state (sessions, proposals, scope config) and for
lifecycle operations like delete_scope.
Sourcepub fn event_bus(&self) -> &E
pub fn event_bus(&self) -> &E
Access the underlying event bus.
Use this to subscribe to consensus events.
Sourcepub async fn create_proposal(
&self,
scope: &Scope,
request: CreateProposalRequest,
) -> Result<Proposal, ConsensusError>
pub async fn create_proposal( &self, scope: &Scope, request: CreateProposalRequest, ) -> Result<Proposal, ConsensusError>
Create a new proposal and start the voting process.
This creates the proposal and sets up a session to track votes. The proposal will expire after the time specified in the request.
Important: The library does not schedule timeouts automatically.
Your application MUST call handle_consensus_timeout
when the proposal’s timeout elapses (e.g. via tokio::time::sleep).
Without this call, proposals with offline voters will remain stuck in the
Active state indefinitely, and the liveness criteria for silent peers
will never take effect.
Configuration is resolved from: proposal config > scope config > global default. If no config is provided, the scope’s default configuration is used.
Sourcepub async fn create_proposal_with_config(
&self,
scope: &Scope,
request: CreateProposalRequest,
config: Option<ConsensusConfig>,
) -> Result<Proposal, ConsensusError>
pub async fn create_proposal_with_config( &self, scope: &Scope, request: CreateProposalRequest, config: Option<ConsensusConfig>, ) -> Result<Proposal, ConsensusError>
Create a new proposal with an explicit ConsensusConfig override.
Pass None to fall back to scope defaults (same as create_proposal).
Sourcepub async fn cast_vote<SN: Signer + Sync + Send>(
&self,
scope: &Scope,
proposal_id: u32,
choice: bool,
signer: SN,
) -> Result<Vote, ConsensusError>
pub async fn cast_vote<SN: Signer + Sync + Send>( &self, scope: &Scope, proposal_id: u32, choice: bool, signer: SN, ) -> Result<Vote, ConsensusError>
Cast a vote on an active proposal.
The vote is cryptographically signed with signer and linked into the
hashgraph chain. Returns the signed Vote for network propagation.
Each voter can only vote once per proposal.
Sourcepub async fn cast_vote_and_get_proposal<SN: Signer + Sync + Send>(
&self,
scope: &Scope,
proposal_id: u32,
choice: bool,
signer: SN,
) -> Result<Proposal, ConsensusError>
pub async fn cast_vote_and_get_proposal<SN: Signer + Sync + Send>( &self, scope: &Scope, proposal_id: u32, choice: bool, signer: SN, ) -> Result<Proposal, ConsensusError>
Cast a vote and return the updated Proposal (with the new vote included).
Convenience method useful for the proposal creator who wants to immediately gossip the updated proposal to peers.
Sourcepub async fn process_incoming_proposal(
&self,
scope: &Scope,
proposal: Proposal,
) -> Result<(), ConsensusError>
pub async fn process_incoming_proposal( &self, scope: &Scope, proposal: Proposal, ) -> Result<(), ConsensusError>
Process a proposal received from the network.
Call this when your networking layer delivers a proposal from another peer. The library performs no I/O — your application must handle gossip/transport and call this method on receipt.
Validates the proposal and all embedded votes, then stores it locally. If enough votes are already present, consensus is reached immediately.
Sourcepub async fn process_incoming_vote(
&self,
scope: &Scope,
vote: Vote,
) -> Result<(), ConsensusError>
pub async fn process_incoming_vote( &self, scope: &Scope, vote: Vote, ) -> Result<(), ConsensusError>
Process a single vote received from the network.
Call this when your networking layer delivers a vote from another peer. Validates the vote (signature, timestamp, chain) and adds it to the corresponding proposal session. May trigger consensus.
Sourcepub async fn handle_consensus_timeout(
&self,
scope: &Scope,
proposal_id: u32,
) -> Result<bool, ConsensusError>
pub async fn handle_consensus_timeout( &self, scope: &Scope, proposal_id: u32, ) -> Result<bool, ConsensusError>
Handle the timeout for a proposal.
The library does not call this automatically. Your application MUST
schedule a timer (e.g. tokio::time::sleep(config.consensus_timeout()))
and invoke this method when it fires. Without this call, proposals with
offline voters will stay in the Active state forever and silent-peer
liveness logic will never run.
At timeout, silent peers are counted toward quorum using the proposal’s
liveness_criteria_yes flag (RFC Section 4, Silent Node Management):
liveness_criteria_yes = true— silent peers count as YESliveness_criteria_yes = false— silent peers count as NO
Returns the consensus result if determinable, or
InsufficientVotesAtTimeout
if the result is a tie after counting silent peers.
Sourcepub async fn scope(
&self,
scope: &Scope,
) -> Result<ScopeConfigBuilderWrapper<Scope, S, E>, ConsensusError>
pub async fn scope( &self, scope: &Scope, ) -> Result<ScopeConfigBuilderWrapper<Scope, S, E>, ConsensusError>
Get a builder for a scope configuration.
§Example
use hashgraph_like_consensus::{scope_config::NetworkType, scope::ScopeID, service::DefaultConsensusService};
use std::time::Duration;
async fn example() -> Result<(), Box<dyn std::error::Error>> {
let service = DefaultConsensusService::default();
let scope = ScopeID::from("my_scope");
// Initialize new scope
service
.scope(&scope)
.await?
.with_network_type(NetworkType::P2P)
.with_threshold(0.75)
.with_timeout(Duration::from_secs(120))
.initialize()
.await?;
// Update existing scope (single field)
service
.scope(&scope)
.await?
.with_threshold(0.8)
.update()
.await?;
Ok(())
}Source§impl<Scope, S, E> ConsensusService<Scope, S, E>
impl<Scope, S, E> ConsensusService<Scope, S, E>
Sourcepub async fn get_scope_stats(&self, scope: &Scope) -> ConsensusStats
pub async fn get_scope_stats(&self, scope: &Scope) -> ConsensusStats
Get statistics about proposals in a scope.
Returns counts of total, active, failed, and finalized proposals. Useful for monitoring and dashboards.
Trait Implementations§
Source§impl<Scope, S, E> Clone for ConsensusService<Scope, S, E>
impl<Scope, S, E> Clone for ConsensusService<Scope, S, E>
Auto Trait Implementations§
impl<Scope, S, E> Freeze for ConsensusService<Scope, S, E>
impl<Scope, S, E> RefUnwindSafe for ConsensusService<Scope, S, E>
impl<Scope, S, E> Send for ConsensusService<Scope, S, E>
impl<Scope, S, E> Sync for ConsensusService<Scope, S, E>
impl<Scope, S, E> Unpin for ConsensusService<Scope, S, E>
impl<Scope, S, E> UnsafeUnpin for ConsensusService<Scope, S, E>where
S: UnsafeUnpin,
E: UnsafeUnpin,
impl<Scope, S, E> UnwindSafe for ConsensusService<Scope, S, E>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more