//! Standard types sent over the wire for [Dealer] shares and [Player] acknowledgements. //! //! [Dealer]: crate::bls12381::dkg::Dealer //! [Player]: crate::bls12381::dkg::Player use crate::{ bls12381::primitives::{group, poly::Public, variant::Variant}, PublicKey, Signature, Signer, }; use bytes::{Buf, BufMut}; use commonware_codec::{varint::UInt, EncodeSize, FixedSize, Read, ReadExt, Write}; use commonware_utils::quorum; /// A [Share] 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]: group::Share /// [Dealer]: crate::bls12381::dkg::Dealer /// [Player]: crate::bls12381::dkg::Player #[derive(Debug, Clone, PartialEq, Eq)] pub struct Share { /// The [Dealer]'s public commitment (coefficients of the polynomial). /// /// [Dealer]: crate::bls12381::dkg::Dealer pub commitment: Public, /// The secret share evaluated for the recipient [Player]. /// /// [Player]: crate::bls12381::dkg::Player pub share: group::Share, } impl Share { /// Create a new [Share] message. pub fn new(commitment: Public, share: group::Share) -> Self { Self { commitment, share } } } impl Write for Share { fn write(&self, buf: &mut impl BufMut) { self.commitment.write(buf); self.share.write(buf); } } impl EncodeSize for Share { fn encode_size(&self) -> usize { self.commitment.encode_size() + self.share.encode_size() } } impl Read for Share { type Cfg = u32; fn read_cfg(buf: &mut impl Buf, t: &u32) -> Result { let q = quorum(*t); Ok(Self { commitment: Public::::read_cfg(buf, &(q as usize))?, share: group::Share::read(buf)?, }) } } /// Acknowledgement message sent by a [Player] node back to the [Dealer] node. /// /// Acknowledges the receipt and verification of a [Share] message. /// Includes a signature to authenticate the acknowledgment. /// /// [Dealer]: crate::bls12381::dkg::Dealer /// [Player]: crate::bls12381::dkg::Player #[derive(Debug, Clone, PartialEq, Eq)] pub struct Ack { /// The public key identifier of the [Player] sending the acknowledgment. /// /// [Player]: crate::bls12381::dkg::Player pub player: u32, /// A signature covering the DKG round, dealer ID, and the [Dealer]'s commitment. /// This confirms the player received and validated the correct share. /// /// [Dealer]: crate::bls12381::dkg::Dealer pub signature: S, } impl Ack { /// Create a new [Ack] message, constructing and signing the payload with the provided [Signer]. pub fn new( namespace: &[u8], signer: &C, player: u32, round: u64, dealer: &C::PublicKey, commitment: &Public, ) -> Self where C: Signer, V: Variant, { let payload = Self::signature_payload::(round, dealer, commitment); let signature = signer.sign(Some(namespace), &payload); Self { player, signature } } /// Verifies the signature in the [Ack] message. pub fn verify>( &self, namespace: &[u8], public_key: &P, round: u64, dealer: &P, commitment: &Public, ) -> bool { let payload = Self::signature_payload::(round, dealer, commitment); public_key.verify(Some(namespace), &payload, &self.signature) } /// Create a signature payload for acking a secret. /// /// This payload consists of the round number, [Dealer]'s public key, and the [Dealer]'s commitment, /// and the signature over this payload is included in the [Ack] message. /// /// [Dealer]: crate::bls12381::dkg::Dealer fn signature_payload( round: u64, dealer: &P, commitment: &Public, ) -> Vec { let mut payload = Vec::with_capacity(u64::SIZE + P::SIZE + commitment.encode_size()); round.write(&mut payload); dealer.write(&mut payload); commitment.write(&mut payload); payload } } impl Write for Ack { fn write(&self, buf: &mut impl BufMut) { UInt(self.player).write(buf); self.signature.write(buf); } } impl EncodeSize for Ack { fn encode_size(&self) -> usize { UInt(self.player).encode_size() + self.signature.encode_size() } } impl Read for Ack { type Cfg = (); fn read_cfg(buf: &mut impl Buf, _: &()) -> Result { Ok(Self { player: UInt::read(buf)?.into(), signature: S::read(buf)?, }) } } #[cfg(test)] mod test { use super::*; use crate::{ bls12381::{ dkg::ops, primitives::{group, poly::Public, variant::MinSig}, }, ed25519::PrivateKey, PrivateKeyExt, Signer, }; use commonware_utils::quorum; use rand::SeedableRng; use rand_chacha::ChaCha8Rng; const ACK_NAMESPACE: &[u8] = b"DKG_ACK"; fn generate_identities(num_peers: u32) -> (Public, Vec<(PrivateKey, group::Share)>) { let mut rng = ChaCha8Rng::seed_from_u64(num_peers as u64); // Generate consensus key let threshold = quorum(num_peers); let (polynomial, shares) = ops::generate_shares::<_, MinSig>(&mut rng, None, num_peers, threshold); // Generate p2p private keys let mut peer_signers = (0..num_peers) .map(|_| PrivateKey::from_rng(&mut rng)) .collect::>(); peer_signers.sort_by_key(|signer| signer.public_key()); let identities = peer_signers.into_iter().zip(shares).collect::>(); (polynomial, identities) } #[test] fn test_share_roundtrip() { const NUM_PARTICIPANTS: u32 = 4; let (commitment, identities) = generate_identities(NUM_PARTICIPANTS); let (_, share) = &identities[0]; let share = Share::::new(commitment.clone(), share.clone()); let mut buf = Vec::with_capacity(share.encode_size()); share.write(&mut buf); let decoded = Share::::read_cfg(&mut buf.as_slice(), &NUM_PARTICIPANTS).unwrap(); assert_eq!(decoded, share); } #[test] fn test_ack_roundtrip() { const NUM_PARTICIPANTS: u32 = 4; let (commitment, identities) = generate_identities(NUM_PARTICIPANTS); let (signer, _) = &identities[0]; let ack = Ack::new::( ACK_NAMESPACE, signer, 1337, 42, &signer.public_key(), &commitment, ); let mut buf = Vec::with_capacity(ack.encode_size()); ack.write(&mut buf); let decoded = Ack::<::Signature>::read_cfg(&mut buf.as_slice(), &()).unwrap(); assert_eq!(decoded, ack); } }