//! Types for the DKG/reshare protocol. use bytes::{Buf, BufMut}; use commonware_codec::{varint::UInt, EncodeSize, FixedSize, RangeCfg, Read, ReadExt, Write}; use commonware_cryptography::{ bls12381::{ dkg::types::{Ack, Share}, primitives::{group, poly::Public, variant::Variant}, }, Signature, Signer, Verifier, }; /// The result of a resharing operation from the local [Dealer]. /// /// [Dealer]: commonware_cryptography::bls12381::dkg::Dealer #[derive(Clone)] pub struct DealOutcome { /// The public key of the dealer. pub dealer: C::PublicKey, /// The dealer's signature over the resharing round, commitment, acks, and reveals. pub dealer_signature: C::Signature, /// The round of the resharing operation. pub round: u64, /// The new group public key polynomial. pub commitment: Public, /// All signed acknowledgements from participants. pub acks: Vec>, /// Any revealed secret shares. pub reveals: Vec, } impl DealOutcome { /// Creates a new [DealOutcome], signing its inner payload with the [commonware_cryptography::bls12381::dkg::Dealer]'s [Signer]. pub fn new( dealer_signer: &C, namespace: &[u8], round: u64, commitment: Public, acks: Vec>, reveals: Vec, ) -> Self { // Sign the resharing outcome let payload = Self::signature_payload_from_parts(round, &commitment, &acks, &reveals); let dealer_signature = dealer_signer.sign(Some(namespace), payload.as_ref()); Self { dealer: dealer_signer.public_key(), dealer_signature, round, commitment, acks, reveals, } } /// Verifies the [DealOutcome]'s signature. pub fn verify(&self, namespace: &[u8]) -> bool { let payload = Self::signature_payload_from_parts( self.round, &self.commitment, &self.acks, &self.reveals, ); self.dealer .verify(Some(namespace), &payload, &self.dealer_signature) } /// Returns the payload that was signed by the dealer, formed from raw parts. fn signature_payload_from_parts( round: u64, commitment: &Public, acks: &[Ack], reveals: &Vec, ) -> Vec { let mut buf = Vec::with_capacity( UInt(round).encode_size() + commitment.encode_size() + acks.encode_size() + reveals.encode_size(), ); UInt(round).write(&mut buf); commitment.write(&mut buf); acks.write(&mut buf); reveals.write(&mut buf); buf } } impl Write for DealOutcome { fn write(&self, buf: &mut impl bytes::BufMut) { self.dealer.write(buf); self.dealer_signature.write(buf); UInt(self.round).write(buf); self.commitment.write(buf); self.acks.write(buf); self.reveals.write(buf); } } impl EncodeSize for DealOutcome { fn encode_size(&self) -> usize { self.dealer.encode_size() + self.dealer_signature.encode_size() + UInt(self.round).encode_size() + self.commitment.encode_size() + self.acks.encode_size() + self.reveals.encode_size() } } impl Read for DealOutcome { type Cfg = usize; fn read_cfg( buf: &mut impl bytes::Buf, cfg: &Self::Cfg, ) -> Result { Ok(Self { dealer: C::PublicKey::read(buf)?, dealer_signature: C::Signature::read(buf)?, round: UInt::read(buf)?.into(), commitment: Public::::read_cfg(buf, cfg)?, acks: Vec::>::read_cfg(buf, &(RangeCfg::from(0..=usize::MAX), ()))?, reveals: Vec::::read_cfg(buf, &(RangeCfg::from(0..=usize::MAX), ()))?, }) } } /// Represents a top-level message for the Distributed Key Generation (DKG) protocol, /// typically sent over a dedicated DKG communication channel. /// /// It encapsulates a specific round number and a payload containing the actual /// DKG protocol message content. #[derive(Clone, Debug, PartialEq)] pub struct Dkg { pub round: u64, pub payload: Payload, } impl Write for Dkg { fn write(&self, buf: &mut impl BufMut) { UInt(self.round).write(buf); self.payload.write(buf); } } impl Read for Dkg { type Cfg = u32; fn read_cfg(buf: &mut impl Buf, num_players: &u32) -> Result { let round = UInt::read(buf)?.into(); let payload = Payload::read_cfg(buf, num_players)?; Ok(Self { round, payload }) } } impl EncodeSize for Dkg { fn encode_size(&self) -> usize { UInt(self.round).encode_size() + self.payload.encode_size() } } const SHARE_TAG: u8 = 0; const ACK_TAG: u8 = 1; /// Defines the different types of messages exchanged during the DKG protocol. /// /// This enum is used as the `payload` field within the [Dkg] message struct. /// The generic parameter `Sig` represents the type used for signatures in acknowledgments. #[derive(Clone, Debug, PartialEq)] pub enum Payload { /// Message sent by a dealer node to a player node. /// /// Contains the dealer's public commitment to their polynomial and the specific /// share calculated for the receiving player. Share(Share), /// Message sent by a player node back to the dealer node. /// /// Acknowledges the receipt and verification of a [Payload::Share] message. /// Includes a signature to authenticate the acknowledgment. Ack(Ack), } impl Payload { /// Lifts the [Payload] into a [Dkg] message for a specific round. pub fn into_message(self, round: u64) -> Dkg { Dkg { round, payload: self, } } } impl Write for Payload { fn write(&self, buf: &mut impl BufMut) { match self { Payload::Share(inner) => { buf.put_u8(SHARE_TAG); inner.write(buf); } Payload::Ack(inner) => { buf.put_u8(ACK_TAG); inner.write(buf); } } } } impl Read for Payload { type Cfg = u32; fn read_cfg(buf: &mut impl Buf, p: &u32) -> Result { let tag = u8::read(buf)?; let result = match tag { SHARE_TAG => Payload::Share(Share::read_cfg(buf, p)?), ACK_TAG => Payload::Ack(Ack::read(buf)?), _ => return Err(commonware_codec::Error::InvalidEnum(tag)), }; Ok(result) } } impl EncodeSize for Payload { fn encode_size(&self) -> usize { u8::SIZE + match self { Payload::Share(inner) => inner.encode_size(), Payload::Ack(inner) => inner.encode_size(), } } }