//! Byzantine participant that only sends `nullify` votes in response to proposals. //! //! This actor observes incoming `Vote` messages and, whenever it receives a //! `Notarize` proposal for some round, it broadcasts a signed `Nullify` for that //! same round. It does not emit any `Finalize` messages. use crate::simplex::{ scheme::Scheme, types::{Nullify, Vote}, }; use commonware_codec::{DecodeExt, Encode}; use commonware_cryptography::{certificate, Hasher}; use commonware_p2p::{Receiver, Recipients, Sender}; use commonware_runtime::{spawn_cell, ContextCell, Handle, Spawner}; use std::marker::PhantomData; use tracing::debug; pub struct Config { pub scheme: S, pub namespace: Vec, } pub struct NullifyOnly, H: Hasher> { context: ContextCell, scheme: S, namespace: Vec, _hasher: PhantomData, } impl, H: Hasher> NullifyOnly { pub fn new(context: E, cfg: Config) -> Self { Self { context: ContextCell::new(context), scheme: cfg.scheme, namespace: cfg.namespace, _hasher: PhantomData, } } pub fn start(mut self, vote_network: (impl Sender, impl Receiver)) -> Handle<()> { spawn_cell!(self.context, self.run(vote_network).await) } async fn run(self, vote_network: (impl Sender, impl Receiver)) { let (mut sender, mut receiver) = vote_network; while let Ok((s, msg)) = receiver.recv().await { // Parse message let msg = match Vote::::decode(msg) { Ok(msg) => msg, Err(err) => { debug!(?err, sender = ?s, "failed to decode message"); continue; } }; // Respond with only a `Nullify` vote when a proposal is observed. if let Vote::Notarize(notarize) = msg { let nullify = Nullify::sign::(&self.scheme, &self.namespace, notarize.round()) .unwrap(); let msg = Vote::::Nullify(nullify).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); } } } }