tempo_commonware_node/dkg/ceremony/
payload.rs

1//! Objects that are created as part of a ceremony and distributed.
2
3use bytes::{Buf, BufMut};
4use commonware_codec::{EncodeSize, FixedSize as _, Read, ReadExt as _, Write, varint::UInt};
5use commonware_consensus::types::Epoch;
6use commonware_cryptography::bls12381::primitives::{group, poly::Public, variant::MinSig};
7use commonware_utils::quorum;
8use tempo_dkg_onchain_artifacts::Ack;
9
10/// The actual message that is sent over p2p.
11#[derive(Clone, Debug, PartialEq)]
12pub(super) struct Message {
13    pub(super) epoch: Epoch,
14    pub(super) payload: Payload,
15}
16
17impl Write for Message {
18    fn write(&self, buf: &mut impl BufMut) {
19        UInt(self.epoch).write(buf);
20        self.payload.write(buf);
21    }
22}
23
24impl Read for Message {
25    type Cfg = u32;
26
27    fn read_cfg(buf: &mut impl Buf, num_players: &u32) -> Result<Self, commonware_codec::Error> {
28        let epoch = UInt::read(buf)?.into();
29        let payload = Payload::read_cfg(buf, num_players)?;
30        Ok(Self { epoch, payload })
31    }
32}
33
34impl EncodeSize for Message {
35    fn encode_size(&self) -> usize {
36        UInt(self.epoch).encode_size() + self.payload.encode_size()
37    }
38}
39
40/// The two kinds of messages that are being exchanged during a ceremony.
41#[derive(Clone, Debug, PartialEq)]
42pub(super) enum Payload {
43    /// Message from dealer to player.
44    ///
45    /// Contains the commitment / public polynomial generated by the dealer for
46    /// the a ceremony and corresponding share of the private key for the
47    /// receiving player.
48    Share(Share),
49
50    /// Message from player to dealer.
51    ///
52    /// Acknowledges the receipt and verification of a [`Share`].
53    ///
54    /// Contains a signature of dealer and commitment to authenticate the
55    /// acknowledged.
56    Ack(Box<Ack>),
57}
58
59impl From<Ack> for Payload {
60    fn from(value: Ack) -> Self {
61        Self::Ack(value.into())
62    }
63}
64
65impl From<Share> for Payload {
66    fn from(value: Share) -> Self {
67        Self::Share(value)
68    }
69}
70
71impl Write for Payload {
72    fn write(&self, buf: &mut impl BufMut) {
73        match self {
74            Self::Share(inner) => {
75                buf.put_u8(SHARE_TAG);
76                inner.write(buf);
77            }
78            Self::Ack(inner) => {
79                buf.put_u8(ACK_TAG);
80                inner.write(buf);
81            }
82        }
83    }
84}
85
86const SHARE_TAG: u8 = 0;
87const ACK_TAG: u8 = 1;
88
89impl Read for Payload {
90    type Cfg = u32;
91
92    fn read_cfg(buf: &mut impl Buf, p: &u32) -> Result<Self, commonware_codec::Error> {
93        let tag = u8::read(buf)?;
94        let result = match tag {
95            SHARE_TAG => Self::Share(Share::read_cfg(buf, p)?),
96            ACK_TAG => Self::Ack(Ack::read(buf)?.into()),
97            _ => return Err(commonware_codec::Error::InvalidEnum(tag)),
98        };
99        Ok(result)
100    }
101}
102
103impl EncodeSize for Payload {
104    fn encode_size(&self) -> usize {
105        u8::SIZE
106            + match self {
107                Self::Share(inner) => inner.encode_size(),
108                Self::Ack(inner) => inner.encode_size(),
109            }
110    }
111}
112
113/// A message from a dealer to a player.
114///
115/// Contains the commitment and one of the shares the dealer generated for the
116/// current ceremony.
117///
118/// The receipt of this message is acknowledged by the player returning an
119/// [`Ack`] to the dealer.
120#[derive(Debug, Clone, PartialEq, Eq)]
121pub(super) struct Share {
122    /// The dealer's generated public polyonimal.
123    pub(super) commitment: Public<MinSig>,
124    /// The secret share generated for the player.
125    pub(super) share: group::Share,
126}
127
128impl Write for Share {
129    fn write(&self, buf: &mut impl BufMut) {
130        self.commitment.write(buf);
131        self.share.write(buf);
132    }
133}
134
135impl EncodeSize for Share {
136    fn encode_size(&self) -> usize {
137        self.commitment.encode_size() + self.share.encode_size()
138    }
139}
140
141impl Read for Share {
142    type Cfg = u32;
143
144    fn read_cfg(buf: &mut impl Buf, t: &u32) -> Result<Self, commonware_codec::Error> {
145        let q = quorum(*t);
146        Ok(Self {
147            commitment: Public::<MinSig>::read_cfg(buf, &(q as usize))?,
148            share: group::Share::read(buf)?,
149        })
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use commonware_codec::{Encode as _, Read as _};
156    use commonware_cryptography::{
157        PrivateKeyExt as _, Signer as _,
158        bls12381::{dkg, primitives::variant::MinSig},
159        ed25519::{PrivateKey, PublicKey},
160    };
161    use commonware_utils::{set::Ordered, union};
162    use rand::{SeedableRng as _, rngs::StdRng};
163
164    use crate::dkg::ceremony::ACK_NAMESPACE;
165
166    use super::Ack;
167
168    fn three_public_keys() -> Ordered<PublicKey> {
169        vec![
170            PrivateKey::from_seed(0).public_key(),
171            PrivateKey::from_seed(1).public_key(),
172            PrivateKey::from_seed(2).public_key(),
173        ]
174        .into()
175    }
176
177    #[test]
178    fn roundtrip_ack() {
179        let (_, commitment, _) = dkg::Dealer::<_, MinSig>::new(
180            &mut StdRng::from_seed([0; 32]),
181            None,
182            three_public_keys(),
183        );
184        let player = PrivateKey::from_seed(0);
185        let dealer = PrivateKey::from_seed(1).public_key();
186        let ack = Ack::new(
187            &union(b"test", ACK_NAMESPACE),
188            player.clone(),
189            player.public_key(),
190            42,
191            &dealer,
192            &commitment,
193        );
194
195        let bytes = ack.encode();
196
197        assert_eq!(Ack::read_cfg(&mut bytes.as_ref(), &()).unwrap(), ack);
198    }
199}