Skip to main content

ConsensusService

Struct ConsensusService 

Source
pub struct ConsensusService<Scope, S, E>
where Scope: ConsensusScope, S: ConsensusStorage<Scope>, E: ConsensusEventBus<Scope>,
{ /* 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>>

Source

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>
where Scope: ConsensusScope, S: ConsensusStorage<Scope>, E: ConsensusEventBus<Scope>,

Source

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.

Source

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.

Source

pub fn event_bus(&self) -> &E

Access the underlying event bus.

Use this to subscribe to consensus events.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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 YES
  • liveness_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.

Source

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>
where Scope: ConsensusScope, S: ConsensusStorage<Scope>, E: ConsensusEventBus<Scope>,

Source

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>
where Scope: ConsensusScope, S: ConsensusStorage<Scope>, E: ConsensusEventBus<Scope>,

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl<Scope, S, E> Freeze for ConsensusService<Scope, S, E>
where S: Freeze, E: Freeze,

§

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>
where S: Unpin, E: Unpin, Scope: Unpin,

§

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>
where S: UnwindSafe, E: UnwindSafe, Scope: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more