tempo_commonware_node/dkg/ceremony/
persisted.rs

1//! Information about a ceremony that is persisted to disk.
2
3use std::collections::BTreeMap;
4
5use bytes::Buf;
6use commonware_codec::{EncodeSize, RangeCfg, Read, Write, varint::UInt};
7use commonware_cryptography::{
8    bls12381::primitives::{group, poly::Public, variant::MinSig},
9    ed25519::PublicKey,
10};
11use commonware_utils::quorum;
12
13use super::IntermediateOutcome;
14
15/// Information on a ceremony that is persisted to disk.
16#[derive(Clone, Default, Debug, PartialEq, Eq)]
17pub(in crate::dkg) struct State {
18    pub(super) num_players: u16,
19
20    /// Tracks the local dealing if we participate as a dealer.
21    pub(super) dealing: Option<Dealing>,
22
23    /// Tracks the shares received from other dealers, if we are a player.
24    pub(super) received_shares: Vec<(PublicKey, Public<MinSig>, group::Share)>,
25
26    pub(super) dealing_outcome: Option<IntermediateOutcome>,
27
28    pub(super) outcomes: Vec<IntermediateOutcome>,
29}
30
31impl Write for State {
32    fn write(&self, buf: &mut impl bytes::BufMut) {
33        UInt(self.num_players).write(buf);
34        self.dealing.write(buf);
35        self.received_shares.write(buf);
36        self.dealing_outcome.write(buf);
37        self.outcomes.write(buf);
38    }
39}
40
41impl EncodeSize for State {
42    fn encode_size(&self) -> usize {
43        UInt(self.num_players).encode_size()
44            + self.dealing.encode_size()
45            + self.received_shares.encode_size()
46            + self.dealing_outcome.encode_size()
47            + self.outcomes.encode_size()
48    }
49}
50
51impl Read for State {
52    // The consensus quorum
53    type Cfg = ();
54
55    fn read_cfg(
56        buf: &mut impl bytes::Buf,
57        _cfg: &Self::Cfg,
58    ) -> Result<Self, commonware_codec::Error> {
59        let num_players = UInt::read_cfg(buf, &())?.into();
60        let dealing = Option::<Dealing>::read_cfg(buf, &(quorum(num_players as u32) as usize))?;
61        let received_shares = Vec::<(PublicKey, Public<MinSig>, group::Share)>::read_cfg(
62            buf,
63            &(
64                RangeCfg::from(0..usize::MAX),
65                ((), quorum(num_players as u32) as usize, ()),
66            ),
67        )?;
68        let dealing_outcome = Option::<IntermediateOutcome>::read_cfg(buf, &())?;
69        let outcomes =
70            Vec::<IntermediateOutcome>::read_cfg(buf, &(RangeCfg::from(0..usize::MAX), ()))?;
71        Ok(Self {
72            num_players,
73            dealing,
74            received_shares,
75            dealing_outcome,
76            outcomes,
77        })
78    }
79}
80
81/// The local dealing of the current ceremony.
82///
83/// Here, the dealer tracks its generated commitment and shares, as well
84/// as the acknowledgments it received for its shares.
85#[derive(Clone, Debug, PartialEq, Eq)]
86pub(super) struct Dealing {
87    pub(super) commitment: Public<MinSig>,
88    pub(super) shares: BTreeMap<PublicKey, group::Share>,
89    pub(super) acks: BTreeMap<PublicKey, super::Ack>,
90}
91
92impl EncodeSize for Dealing {
93    fn encode_size(&self) -> usize {
94        self.commitment.encode_size() + self.shares.encode_size() + self.acks.encode_size()
95    }
96}
97
98impl Read for Dealing {
99    type Cfg = usize;
100
101    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
102        let commitment = Public::<MinSig>::read_cfg(buf, cfg)?;
103        let shares = BTreeMap::read_cfg(buf, &(RangeCfg::from(0..usize::MAX), ((), ())))?;
104        let acks = BTreeMap::read_cfg(buf, &(RangeCfg::from(0..usize::MAX), ((), ())))?;
105        Ok(Self {
106            commitment,
107            shares,
108            acks,
109        })
110    }
111}
112
113impl Write for Dealing {
114    fn write(&self, buf: &mut impl bytes::BufMut) {
115        self.commitment.write(buf);
116        self.shares.write(buf);
117        self.acks.write(buf);
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use std::collections::BTreeMap;
124
125    use crate::dkg::ceremony::{ACK_NAMESPACE, Ack, OUTCOME_NAMESPACE};
126
127    use super::{Dealing, State};
128    use commonware_codec::{DecodeExt as _, Encode as _, Read as _};
129    use commonware_cryptography::{
130        PrivateKeyExt as _, Signer,
131        bls12381::{dkg, primitives::variant::MinSig},
132        ed25519::{PrivateKey, PublicKey},
133    };
134    use commonware_utils::{quorum, set::Ordered, union};
135    use rand::{SeedableRng as _, rngs::StdRng};
136    use tempo_dkg_onchain_artifacts::IntermediateOutcome;
137
138    fn four_private_keys() -> Ordered<PrivateKey> {
139        vec![
140            PrivateKey::from_seed(0),
141            PrivateKey::from_seed(1),
142            PrivateKey::from_seed(2),
143            PrivateKey::from_seed(3),
144        ]
145        .into()
146    }
147
148    fn four_public_keys() -> Ordered<PublicKey> {
149        vec![
150            PrivateKey::from_seed(0).public_key(),
151            PrivateKey::from_seed(1).public_key(),
152            PrivateKey::from_seed(2).public_key(),
153            PrivateKey::from_seed(3).public_key(),
154        ]
155        .into()
156    }
157
158    fn dealing(dealer_index: usize) -> Dealing {
159        let (_, commitment, shares) = dkg::Dealer::<_, MinSig>::new(
160            &mut StdRng::from_seed([dealer_index as u8; 32]),
161            None,
162            four_public_keys(),
163        );
164        let shares = four_public_keys().iter().cloned().zip(shares).collect();
165
166        let mut acks = BTreeMap::new();
167        acks.insert(
168            four_public_keys()[0].clone(),
169            Ack::new(
170                &union(b"test", ACK_NAMESPACE),
171                four_private_keys()[0].clone(),
172                four_public_keys()[0].clone(),
173                42,
174                &four_public_keys()[dealer_index],
175                &commitment,
176            ),
177        );
178        acks.insert(
179            four_public_keys()[1].clone(),
180            Ack::new(
181                &union(b"test", ACK_NAMESPACE),
182                four_private_keys()[1].clone(),
183                four_public_keys()[1].clone(),
184                42,
185                &four_public_keys()[dealer_index],
186                &commitment,
187            ),
188        );
189        acks.insert(
190            four_public_keys()[2].clone(),
191            Ack::new(
192                &union(b"test", ACK_NAMESPACE),
193                four_private_keys()[2].clone(),
194                four_public_keys()[2].clone(),
195                42,
196                &four_public_keys()[dealer_index],
197                &commitment,
198            ),
199        );
200        acks.insert(
201            four_public_keys()[3].clone(),
202            Ack::new(
203                &union(b"test", ACK_NAMESPACE),
204                four_private_keys()[3].clone(),
205                four_public_keys()[3].clone(),
206                42,
207                &four_public_keys()[dealer_index],
208                &commitment,
209            ),
210        );
211        Dealing {
212            commitment,
213            shares,
214            acks,
215        }
216    }
217
218    fn dealing_outcome(dealer_index: usize) -> IntermediateOutcome {
219        let mut dealing = dealing(dealer_index);
220
221        IntermediateOutcome::new(
222            4,
223            &four_private_keys()[0],
224            &union(b"test", OUTCOME_NAMESPACE),
225            42,
226            dealing.commitment,
227            dealing.acks.values().cloned().collect(),
228            vec![dealing.shares.pop_last().unwrap().1],
229        )
230    }
231
232    #[test]
233    fn roundtrip_dealing() {
234        let bytes = dealing(0).encode();
235
236        assert_eq!(
237            Dealing::read_cfg(&mut bytes.as_ref(), &(quorum(4) as usize)).unwrap(),
238            dealing(0),
239        )
240    }
241
242    #[test]
243    fn roundtrip_state() {
244        let state = State {
245            num_players: 4,
246            dealing: Some(dealing(0)),
247            received_shares: vec![
248                (
249                    four_public_keys()[1].clone(),
250                    dealing(1).commitment,
251                    dealing(1).shares[&four_public_keys()[0]].clone(),
252                ),
253                (
254                    four_public_keys()[2].clone(),
255                    dealing(2).commitment,
256                    dealing(2).shares[&four_public_keys()[0]].clone(),
257                ),
258            ],
259            dealing_outcome: Some(dealing_outcome(0)),
260            outcomes: vec![dealing_outcome(1), dealing_outcome(2)],
261        };
262
263        let bytes = state.encode().freeze();
264
265        assert_eq!(State::decode(&mut bytes.as_ref()).unwrap(), state);
266    }
267}