1use snafu::ensure;
32
33use crate::Result;
34use crate::error;
35use crate::message::Message;
36use crate::party::{Party, PartyList};
37use crate::session::{Session, SessionParams};
38use crate::share::Share;
39
40const MAX_ROUNDS: u8 = 8;
45
46pub fn run_dkg(scheme: &str, threshold: u32, party_count: usize) -> Result<Vec<Vec<u8>>> {
55 ensure!(party_count > 0, error::EmptyPartyListSnafu {});
56 let roster: Vec<Party> = (0..party_count)
57 .map(|i| Party::inproc(format!("p{i}")))
58 .collect();
59 let parties = PartyList::from_parties(roster);
60 let party_ids: Vec<String> = parties.parties().iter().map(|p| p.id.clone()).collect();
61
62 let mut sessions: Vec<Session> = (0..party_count)
63 .map(|idx| {
64 Session::create(&SessionParams {
65 scheme: scheme.to_string(),
66 parties: parties.clone(),
67 threshold,
68 this_party_idx: idx,
69 local_share: None,
70 message: None,
71 })
72 })
73 .collect::<Result<Vec<_>>>()?;
74
75 drive_to_completion(&mut sessions, &party_ids)?;
76
77 sessions.iter().map(|s| s.result()).collect()
78}
79
80pub fn run_sign(
87 scheme: &str,
88 share_blobs: &[Vec<u8>],
89 threshold: u32,
90 message: &[u8],
91) -> Result<Vec<u8>> {
92 let signer_count = share_blobs.len();
93 ensure!(
94 signer_count as u32 >= threshold,
95 error::ThresholdTooLargeSnafu {
96 threshold,
97 party_count: signer_count,
98 }
99 );
100
101 let roster: Vec<Party> = (0..signer_count)
102 .map(|i| Party::inproc(format!("p{i}")))
103 .collect();
104 let parties = PartyList::from_parties(roster);
105 let party_ids: Vec<String> = parties.parties().iter().map(|p| p.id.clone()).collect();
106
107 let mut sessions: Vec<Session> = (0..signer_count)
108 .map(|idx| {
109 let local_share = Share::new(scheme.to_string(), share_blobs[idx].clone());
110 Session::create(&SessionParams {
111 scheme: scheme.to_string(),
112 parties: parties.clone(),
113 threshold,
114 this_party_idx: idx,
115 local_share: Some(local_share),
116 message: Some(message.to_vec()),
117 })
118 })
119 .collect::<Result<Vec<_>>>()?;
120
121 drive_to_completion(&mut sessions, &party_ids)?;
122
123 sessions[0].result()
124}
125
126fn drive_to_completion(sessions: &mut [Session], party_ids: &[String]) -> Result<()> {
130 let mut outgoing: Vec<Vec<Message>> = Vec::new();
131 for _round in 1..=MAX_ROUNDS {
132 outgoing = step_rounds(sessions, &outgoing, party_ids)?;
133 if sessions.iter().all(|s| s.is_complete()) {
134 return Ok(());
135 }
136 }
137 Err(error::RoundOverflowSnafu { round: MAX_ROUNDS }.build())
138}
139
140fn step_rounds(
143 sessions: &mut [Session],
144 prev_outgoing: &[Vec<Message>],
145 party_ids: &[String],
146) -> Result<Vec<Vec<Message>>> {
147 let n = sessions.len();
148 let mut incoming: Vec<Vec<Message>> = vec![Vec::new(); n];
149 for (sender_pos, outs) in prev_outgoing.iter().enumerate() {
150 for m in outs {
151 for (recv_pos, pid) in party_ids.iter().enumerate() {
152 if recv_pos == sender_pos {
153 continue;
154 }
155 if m.is_for(pid) {
156 incoming[recv_pos].push(m.clone());
157 }
158 }
159 }
160 }
161 let mut next: Vec<Vec<Message>> = Vec::with_capacity(n);
162 for (i, sess) in sessions.iter_mut().enumerate() {
163 let r = sess.round_step(&incoming[i])?;
164 next.push(r.outgoing);
165 }
166 Ok(next)
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172 use crate::party::{Party, PartyList};
173 use crate::registry::{RoundResult, SessionImpl, TcScheme, TcSchemeKind};
174
175 struct ToyScheme;
179
180 impl TcScheme for ToyScheme {
181 fn name(&self) -> &'static str {
182 "test-inprocess-driver"
183 }
184 fn kind(&self) -> TcSchemeKind {
185 TcSchemeKind::Dkg
186 }
187 fn create_session(&self, params: &SessionParams) -> Result<Box<dyn SessionImpl>> {
188 let id = params.parties.get(params.this_party_idx)?.id.clone();
189 Ok(Box::new(ToySession { id, round_done: 0 }))
190 }
191 }
192
193 struct ToySession {
194 id: String,
195 round_done: u8,
196 }
197
198 impl SessionImpl for ToySession {
199 fn round(&mut self, _incoming: &[Message]) -> Result<RoundResult> {
200 self.round_done += 1;
201 if self.round_done == 1 {
202 Ok(RoundResult::new(
203 vec![Message::broadcast(&self.id, 1, vec![0xAA])],
204 false,
205 ))
206 } else {
207 Ok(RoundResult::done())
208 }
209 }
210 fn result(&self) -> Result<Vec<u8>> {
211 Ok(vec![0xAA])
212 }
213 fn destroy(&mut self) {}
214 }
215
216 inventory::submit! {
217 crate::registry::RegisteredScheme {
218 scheme: &ToyScheme as &dyn crate::registry::TcScheme
219 }
220 }
221
222 #[test]
223 fn drive_dkg_two_round_scheme_completes() {
224 let out = run_dkg("test-inprocess-driver", 2, 3).expect("dkg");
225 assert_eq!(out.len(), 3);
226 for blob in &out {
227 assert_eq!(blob, &vec![0xAA]);
228 }
229 }
230
231 #[test]
232 fn drive_dkg_rejects_zero_party_count() {
233 let err = run_dkg("test-inprocess-driver", 0, 0);
234 assert!(err.is_err());
235 }
236
237 #[test]
238 fn drive_sign_unknown_scheme_errors() {
239 let err = run_sign("no-such-scheme", &[vec![1, 2, 3]], 1, b"msg");
240 assert!(err.is_err());
241 }
242
243 #[test]
244 fn drive_sign_below_threshold_errors() {
245 let err = run_sign("test-inprocess-driver", &[], 1, b"msg");
247 assert!(err.is_err());
248 }
249
250 fn _ensure_party_list_send_sync(_list: PartyList) {
251 }
254
255 fn _ensure_party_send_sync(_p: Party) {}
256}