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.

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 subscribe_to_events(&self) -> E::Receiver

Subscribe to events like consensus reached or consensus failed.

Returns a receiver that you can use to listen for events across all scopes. Events are broadcast to all subscribers, so multiple parts of your application can react to consensus outcomes.

Source

pub async fn get_consensus_result( &self, scope: &Scope, proposal_id: u32, ) -> Result<bool, ConsensusError>

Get the final consensus result for a proposal, if it’s been reached.

Returns Ok(true) if consensus was YES, Ok(false) if NO, or Err if consensus hasn’t been reached yet (or the proposal doesn’t exist or is still active).

Source

pub async fn get_active_proposals( &self, scope: &Scope, ) -> Result<Option<Vec<Proposal>>, ConsensusError>

Get all proposals that are still accepting votes.

Source

pub async fn get_reached_proposals( &self, scope: &Scope, ) -> Result<Option<HashMap<u32, bool>>, ConsensusError>

Get all proposals that have reached consensus, along with their results.

Returns a map from proposal ID to result (true for YES, false for NO). Only includes proposals that have finalized - active proposals are not included. Returns None if no proposals have reached consensus.

Source

pub async fn has_sufficient_votes_for_proposal( &self, scope: &Scope, proposal_id: u32, ) -> Result<bool, ConsensusError>

Check if a proposal has collected enough votes to reach consensus.

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

pub async fn handle_consensus_timeout( &self, scope: &Scope, proposal_id: u32, ) -> Result<bool, ConsensusError>

Handle the timeout for a proposal.

First checks if consensus has already been reached and returns the result if so. Otherwise, calculates consensus from current votes. If consensus is reached, marks the session as ConsensusReached and returns the result. If no consensus, marks the session as Failed and returns an error.

Source§

impl<Scope, S, E> ConsensusService<Scope, S, E>
where Scope: ConsensusScope, S: ConsensusStorage<Scope>, E: ConsensusEventBus<Scope>,

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, sets up a session to track votes, and schedules automatic timeout handling. The proposal will expire after the time specified in the request.

Configuration is resolved from: proposal config > scope config > global default. If no config is provided, the scope’s default configuration is used.

§Examples
use hashgraph_like_consensus::{scope::ScopeID, scope_config::NetworkType,
service::DefaultConsensusService, types::CreateProposalRequest};

async fn example() -> Result<(), Box<dyn std::error::Error>> {
    let service = DefaultConsensusService::default();
    let scope = ScopeID::from("my_scope");

    service
        .scope(&scope)
        .await?
        .with_network_type(NetworkType::P2P)
        .with_threshold(0.75)
        .initialize()
        .await?;

    let request = CreateProposalRequest::new(
        "Test Proposal".to_string(),
        "payload".to_string(),
        vec![0u8; 20],
        3,
        100,
        true,
    )?;
    let proposal = service.create_proposal(&scope, request).await?;
    Ok(())
}
Source

pub async fn create_proposal_with_config( &self, scope: &Scope, request: CreateProposalRequest, config: Option<ConsensusConfig>, ) -> Result<Proposal, ConsensusError>

Create a new proposal with explicit configuration override.

This allows you to override the scope’s default configuration for a specific proposal. The override takes precedence over scope config.

§Examples
use hashgraph_like_consensus::{scope::ScopeID,
service::DefaultConsensusService, session::ConsensusConfig, types::CreateProposalRequest};

async fn example() -> Result<(), Box<dyn std::error::Error>> {
    let service = DefaultConsensusService::default();
    let scope = ScopeID::from("my_scope");
    let request = CreateProposalRequest::new(
        "Test Proposal".to_string(),
        "payload".to_string(),
        vec![0u8; 20],
        3,
        100,
        true,
    )?;

    let proposal = service.create_proposal_with_config(
        &scope,
        request,
        Some(ConsensusConfig::p2p())
    ).await?;

    let request2 = CreateProposalRequest::new(
        "Another Proposal".to_string(),
        "payload2".to_string(),
        vec![0u8; 20],
        3,
        100,
        true,
    )?;
    let proposal2 = service.create_proposal_with_config(
        &scope,
        request2,
        None
    ).await?;
    Ok(())
}
Source

pub async fn cast_vote<SN: Signer + Sync>( &self, scope: &Scope, proposal_id: u32, choice: bool, signer: SN, ) -> Result<Vote, ConsensusError>

Cast your vote on a proposal (yes or no).

Vote is cryptographically signed and linked to previous votes in the hashgraph. Returns the signed vote, which you can then send to other peers in the network. Each voter can only vote once per proposal.

Source

pub async fn cast_vote_and_get_proposal<SN: Signer + Sync>( &self, scope: &Scope, proposal_id: u32, choice: bool, signer: SN, ) -> Result<Proposal, ConsensusError>

Cast a vote and immediately get back the updated proposal.

This is a convenience method that combines cast_vote and fetching the proposal. Useful for proposal creator as they can immediately see the proposal with their vote and share it with other peers.

Source

pub async fn process_incoming_proposal( &self, scope: &Scope, proposal: Proposal, ) -> Result<(), ConsensusError>

Process a proposal you received from another peer in the network.

This validates the proposal and all its votes (signatures, vote chains, timestamps), then stores it locally. If it necessary the consensus configuration is resolved from the proposal. If the proposal already has enough votes, consensus is reached immediately and an event is emitted.

Source

pub async fn process_incoming_vote( &self, scope: &Scope, vote: Vote, ) -> Result<(), ConsensusError>

Process a vote you received from another peer.

The vote is validated (signature, timestamp, vote chain) and added to the proposal. If this vote brings the total to the consensus threshold, consensus is reached and an event is emitted.

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§

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

Blanket Implementations§

§

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

§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

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

§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
§

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

§

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

Mutably borrows from an owned value. Read more
§

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

§

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
§

impl<T> From<T> for T

§

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
§

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

§

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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

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

§

type Owned = T

The resulting type after obtaining ownership.
§

fn to_owned(&self) -> T

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

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

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

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

§

type Error = Infallible

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

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

Performs the conversion.
§

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

§

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

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

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