Skip to main content

tempo_dkg_onchain_artifacts/
lib.rs

1//! Items that are written to chain.
2
3use std::num::NonZeroU32;
4
5use bytes::{Buf, BufMut};
6use commonware_codec::{EncodeSize, RangeCfg, Read, ReadExt, Write};
7use commonware_consensus::types::Epoch;
8use commonware_cryptography::{
9    bls12381::{
10        dkg::Output,
11        primitives::{
12            sharing::{ModeVersion, Sharing},
13            variant::{MinSig, Variant},
14        },
15    },
16    ed25519::PublicKey,
17};
18use commonware_utils::{NZU32, ordered};
19
20const MAX_VALIDATORS: NonZeroU32 = NZU32!(u16::MAX as u32);
21
22/// The outcome of a DKG ceremony as it is written to the chain.
23///
24/// This DKG outcome can encode up to [`u16::MAX`] validators. Note that in
25/// practice this far exceeds the maximum size permitted header size and so
26/// is likely out of reach.
27#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct OnchainDkgOutcome {
29    /// The epoch for which this outcome is used.
30    pub epoch: Epoch,
31
32    /// The output of the DKG ceremony. Contains the shared public polynomial,
33    /// and the players in the ceremony (which will be the dealers for the
34    /// epoch encoded with this output).
35    pub output: Output<MinSig, PublicKey>,
36
37    /// The next players. These will be the players in the DKG ceremony running
38    /// during `epoch`.
39    pub next_players: ordered::Set<PublicKey>,
40
41    /// Whether the next DKG ceremony should be a full ceremony (new polynomial)
42    /// instead of a reshare. Set when `nextFullDkgCeremony == epoch`.
43    pub is_next_full_dkg: bool,
44}
45
46impl OnchainDkgOutcome {
47    pub fn dealers(&self) -> &ordered::Set<PublicKey> {
48        self.output.dealers()
49    }
50
51    pub fn players(&self) -> &ordered::Set<PublicKey> {
52        self.output.players()
53    }
54
55    pub fn next_players(&self) -> &ordered::Set<PublicKey> {
56        &self.next_players
57    }
58
59    pub fn sharing(&self) -> &Sharing<MinSig> {
60        self.output.public()
61    }
62
63    pub fn network_identity(&self) -> &<MinSig as Variant>::Public {
64        self.sharing().public()
65    }
66}
67
68impl Write for OnchainDkgOutcome {
69    fn write(&self, buf: &mut impl BufMut) {
70        self.epoch.write(buf);
71        self.output.write(buf);
72        self.next_players.write(buf);
73        self.is_next_full_dkg.write(buf);
74    }
75}
76
77impl Read for OnchainDkgOutcome {
78    type Cfg = ();
79
80    fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
81        let epoch = ReadExt::read(buf)?;
82        let output = Read::read_cfg(buf, &(MAX_VALIDATORS, ModeVersion::v0()))?;
83        let next_players = Read::read_cfg(
84            buf,
85            &(RangeCfg::from(1..=(MAX_VALIDATORS.get() as usize)), ()),
86        )?;
87        let is_next_full_dkg = ReadExt::read(buf)?;
88        Ok(Self {
89            epoch,
90            output,
91            next_players,
92            is_next_full_dkg,
93        })
94    }
95}
96
97impl EncodeSize for OnchainDkgOutcome {
98    fn encode_size(&self) -> usize {
99        self.epoch.encode_size()
100            + self.output.encode_size()
101            + self.next_players.encode_size()
102            + self.is_next_full_dkg.encode_size()
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use std::iter::repeat_with;
109
110    use commonware_codec::{Encode as _, ReadExt as _};
111    use commonware_consensus::types::Epoch;
112    use commonware_cryptography::{Signer as _, bls12381::dkg, ed25519::PrivateKey};
113    use commonware_math::algebra::Random as _;
114    use commonware_utils::{N3f1, TryFromIterator as _, ordered};
115    use rand_08::SeedableRng as _;
116
117    use super::OnchainDkgOutcome;
118
119    #[test]
120    fn onchain_dkg_outcome_roundtrip() {
121        let mut rng = rand_08::rngs::StdRng::seed_from_u64(42);
122
123        let mut player_keys = repeat_with(|| PrivateKey::random(&mut rng))
124            .take(10)
125            .collect::<Vec<_>>();
126        player_keys.sort_by_key(|key| key.public_key());
127        let (output, _shares) = dkg::deal::<_, _, N3f1>(
128            &mut rng,
129            Default::default(),
130            ordered::Set::try_from_iter(player_keys.iter().map(|key| key.public_key())).unwrap(),
131        )
132        .unwrap();
133
134        let on_chain = OnchainDkgOutcome {
135            epoch: Epoch::new(42),
136            output,
137            next_players: ordered::Set::try_from_iter(
138                player_keys.iter().map(|key| key.public_key()),
139            )
140            .unwrap(),
141            is_next_full_dkg: false,
142        };
143        let bytes = on_chain.encode();
144        assert_eq!(
145            OnchainDkgOutcome::read(&mut bytes.as_ref()).unwrap(),
146            on_chain,
147        );
148    }
149}