1use std::collections::BTreeMap;
2
3use ark_ff::{PrimeField, UniformRand, Zero};
11use rand::thread_rng;
12
13#[derive(Debug, Clone)]
14pub struct RoundOne {
15 commitments: Vec<decaf377::Element>,
17 from_participant: u32,
18
19 ri: decaf377::Element,
21 ui: decaf377::Fr,
22}
23
24#[derive(Debug, Clone)]
25pub struct RoundTwo {
26 pub for_participant: u32,
27 pub from_participant: u32,
28 pub share: Share,
29}
30
31#[derive(Debug, Clone)]
32pub struct Share {
33 pub x: u32,
34 pub y: decaf377::Fr,
35}
36
37#[derive(Debug, Clone)]
38pub struct DKGOutput {
39 pub group_public_key: decaf377::Element,
40 pub private_share: decaf377::Fr,
41 pub participant_index: u32,
42}
43
44#[derive(Clone)]
45pub struct Participant {
46 secret_coeffs: Vec<decaf377::Fr>,
47 pub commitments: Vec<decaf377::Element>,
48 pub index: u32,
49
50 my_share: Share,
51 counterparty_shares: Vec<Share>,
52 counterparty_commitments: BTreeMap<u32, Vec<decaf377::Element>>,
53}
54
55fn evaluate_polynomial(x: decaf377::Fr, coeffs: &[decaf377::Fr]) -> decaf377::Fr {
58 coeffs
59 .iter()
60 .rev()
61 .fold(decaf377::Fr::zero(), |acc, coeff| acc * x + coeff)
62}
63
64impl Participant {
65 pub fn new(index: u32, t: u32) -> Self {
66 let rng = &mut thread_rng();
67 let secret_coeffs = (0..t).map(|_| decaf377::Fr::rand(rng)).collect::<Vec<_>>();
68 let public_commitments = secret_coeffs
69 .iter()
70 .map(|coeff| coeff * decaf377::basepoint())
71 .collect::<Vec<_>>();
72
73 Participant {
74 commitments: public_commitments,
75 my_share: Share {
76 x: index,
77 y: evaluate_polynomial(decaf377::Fr::from(index), &secret_coeffs),
78 },
79 secret_coeffs,
80 index,
81 counterparty_shares: Vec::new(),
82 counterparty_commitments: BTreeMap::new(),
83 }
84 }
85 pub fn round_one(&self) -> RoundOne {
86 let aio_commitment = self.secret_coeffs[0] * decaf377::basepoint();
87
88 let k = decaf377::Fr::rand(&mut thread_rng());
90 let ri = k * decaf377::basepoint();
91 let ci = blake2b_simd::Params::default()
92 .personal(b"keygen_challenge")
93 .to_state()
94 .update(&self.index.to_le_bytes())
95 .update("TODO: ANTI-REPLAY CONTEXT".as_bytes())
96 .update(aio_commitment.vartime_compress().0.as_ref())
97 .update(ri.vartime_compress().0.as_ref())
98 .finalize();
99
100 let ui = k + self.secret_coeffs[0] * decaf377::Fr::from_le_bytes_mod_order(ci.as_bytes());
101 RoundOne {
102 commitments: self.commitments.clone(),
103 ri,
104 ui,
105 from_participant: self.index,
106 }
107 }
108 pub fn verify_roundone(&mut self, others: Vec<RoundOne>) -> Result<(), anyhow::Error> {
109 let context_string = "TODO: ANTI-REPLAY CONTEXT";
110 for participant in others.iter() {
111 if participant.from_participant == self.index {
112 continue;
113 }
114 let ci = blake2b_simd::Params::default()
116 .personal(b"keygen_challenge")
117 .to_state()
118 .update(&participant.from_participant.to_le_bytes())
119 .update(context_string.as_bytes())
120 .update(participant.commitments[0].vartime_compress().0.as_ref())
121 .update(participant.ri.vartime_compress().0.as_ref())
122 .finalize();
123
124 let ci =
125 participant.commitments[0] * -decaf377::Fr::from_le_bytes_mod_order(ci.as_bytes());
126
127 if participant.ri != (decaf377::basepoint() * participant.ui) + ci {
129 return Err(anyhow::anyhow!("could not verify participant's key share"));
130 }
131
132 self.counterparty_commitments.insert(
134 participant.from_participant,
135 participant.commitments.clone(),
136 );
137 }
138 Ok(())
139 }
140
141 pub fn round_two(&self, counterparty_index: u32) -> RoundTwo {
142 let fi = evaluate_polynomial(decaf377::Fr::from(counterparty_index), &self.secret_coeffs);
143
144 RoundTwo {
145 for_participant: counterparty_index,
146 from_participant: self.index,
147 share: Share {
148 x: counterparty_index,
149 y: fi,
150 },
151 }
152 }
153
154 pub fn verify_and_add_roundtwo_response(
155 &mut self,
156 counterparty_response: &RoundTwo,
157 ) -> Result<(), anyhow::Error> {
158 let commitments = self
159 .counterparty_commitments
160 .get(&counterparty_response.from_participant)
161 .ok_or(anyhow::anyhow!("counterparty commitments not found"))?;
162
163 counterparty_response.verify(commitments)?;
165
166 self.counterparty_shares
167 .push(counterparty_response.share.clone());
168
169 Ok(())
170 }
171
172 pub fn finalize(&self) -> Result<DKGOutput, anyhow::Error> {
173 let mut group_public_key = self.commitments[0];
175 for commitment in self.counterparty_commitments.values() {
176 group_public_key = group_public_key + commitment[0];
177 }
178
179 let mut private_share = self.my_share.y;
181 for other_share in self.counterparty_shares.iter() {
182 private_share = private_share + other_share.y;
183 }
184
185 Ok(DKGOutput {
186 group_public_key,
187 private_share,
188 participant_index: self.index,
189 })
190 }
191}
192
193impl RoundTwo {
194 pub fn verify(
195 &self,
196 counterparty_commitments: &[decaf377::Element],
197 ) -> Result<(), anyhow::Error> {
198 let gfli = self.share.y * decaf377::basepoint();
201
202 let result = counterparty_commitments
204 .iter()
205 .rev()
206 .fold(decaf377::Element::default(), |acc, commitment| {
207 acc * decaf377::Fr::from(self.for_participant) + commitment
208 });
209 if result != gfli {
210 Err(anyhow::anyhow!("verification failed"))?
211 }
212
213 Ok(())
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_roundone_generate_verify() {
223 let t = 3;
224 let n = 5;
225
226 let mut participants = Vec::new();
227 for i in 0..n {
228 participants.push(Participant::new(i, t));
229 }
230
231 let mut round1_messages = Vec::new();
232 for participant in participants.iter() {
233 round1_messages.push(participant.round_one());
234 }
235 for mut participant in participants {
236 participant
237 .verify_roundone(round1_messages.clone())
238 .unwrap();
239 }
240 }
241 #[test]
242 fn test_full_dkg() {
243 let t = 3;
244 let n = 5;
245
246 let mut participants = Vec::new();
247 for i in 1..n + 1 {
248 participants.push(Participant::new(i, t));
249 }
250
251 let mut round1_messages = Vec::new();
252 for participant in participants.iter() {
253 round1_messages.push(participant.round_one());
254 }
255 for participant in participants.iter_mut() {
256 participant
257 .verify_roundone(round1_messages.clone())
258 .unwrap();
259 }
260
261 let other_participants = participants.clone();
262
263 for (i, participant) in participants.iter_mut().enumerate() {
265 for (l, participant_other) in other_participants.iter().enumerate() {
266 if participant.index == participant_other.index {
267 continue;
268 }
269 let round2_message = participant_other.round_two(participant.index);
270 println!("verifying round two message: {:?}", round2_message);
271 participant
272 .verify_and_add_roundtwo_response(&round2_message)
273 .unwrap();
274 }
275 }
276
277 let dkg_pubkey = participants[0].finalize().unwrap().group_public_key;
278 for participant in participants.iter() {
279 if participant.finalize().unwrap().group_public_key != dkg_pubkey {
280 panic!("group public key mismatch");
281 }
282 }
283 }
284}