commonware-consensus 2026.10.0

Order opaque messages in a Byzantine environment.
Documentation
//! Consensus-relay plumbing shared by the marshal application wrappers.

use crate::{
    marshal::{
        application::gates::{Gates, Staged},
        core::{Mailbox, Variant},
    },
    simplex::Plan,
};
use commonware_actor::Feedback;
use commonware_cryptography::certificate::Scheme;
use commonware_p2p::Recipients;
use tracing::debug;

/// Relays a consensus broadcast [`Plan`] through marshal for a proposal staged in `gates`.
///
/// A prepare plan sends the staged candidate to all peers, at most once, and keeps
/// it staged without storing it. A propose plan locks the staged proposal in: it sends
/// the proposal to all peers unless a prepare plan already did, and persists
/// it either way, delivering the durable-sync handle through the staged ack. A
/// forward plan re-sends a stored block to the requested recipients.
///
/// A propose plan that finds nothing staged sends nothing. Certification claims our own
/// proposal only after a notarization, which needs our vote, and consensus casts that vote
/// only after the propose plan. Within a process, a proposal is therefore missing only when
/// its round is decided, and a prepare plan may already have sent the block. Prepare and
/// propose plans send nothing for a round at or below the decided cutoff of [`Gates`].
pub(crate) fn broadcast<S, V>(
    gates: &Gates<V::Commitment, V::Block>,
    marshal: &Mailbox<S, V>,
    commitment: V::Commitment,
    plan: Plan<S::PublicKey>,
) -> Feedback
where
    S: Scheme,
    V: Variant,
{
    match plan {
        Plan::Prepare { round } => {
            let Some(block) = gates.send_staged(round, commitment) else {
                debug!(%round, %commitment, "no unsent staged candidate to relay early");
                return Feedback::Ok;
            };
            marshal.prepared(round, block, Recipients::All)
        }
        Plan::Propose { round } => {
            let Some((Staged { block, ack }, sent)) = gates.take_staged(round, commitment) else {
                debug!(%round, %commitment, "no staged proposal to relay");
                return Feedback::Ok;
            };
            if sent {
                marshal.verified_deferred(round, block, ack);
                return Feedback::Ok;
            }
            marshal.proposed(round, block, Recipients::All, ack)
        }
        Plan::Forward { round, recipients } => marshal.forward(round, commitment, recipients),
    }
}