1use crate::dkg::{
140 ParticipantsProvider, Registrar, ReshareBlock, SecretStore,
141 fence::Fence,
142 network::{Directory, Manager},
143 reshare::{Mailbox, Message, metrics::Metrics as ReshareMetrics, store::Store},
144 state_sync::{self, Plan as StateSyncPlan},
145 types::EpochInfo,
146};
147use commonware_actor::mailbox::{self as actor_mailbox, Receiver as MailboxReceiver};
148use commonware_consensus::{
149 Heightable as _,
150 marshal::core::{CommitmentFallback, Mailbox as MarshalMailbox, Variant as MarshalVariant},
151 simplex::scheme::Scheme as SimplexScheme,
152 types::{EpochPhase, FixedEpocher},
153};
154use commonware_cryptography::{
155 BatchVerifier, PublicKey, Signer,
156 bls12381::{
157 dkg::feldman_desmedt::Reveal,
158 primitives::{sharing::Mode as SharingMode, variant::Variant as BlsVariant},
159 },
160 certificate::Scheme,
161};
162use commonware_p2p::{Blocker, Receiver, Sender, utils::mux::Muxer};
163use commonware_parallel::Strategy;
164use commonware_runtime::{
165 BufferPooler, Clock, ContextCell, Handle, Metrics, Spawner, Storage, spawn_cell,
166};
167use commonware_utils::{Acknowledgement, acknowledgement::Exact, ordered::Set};
168use rand_core::CryptoRng;
169use std::{
170 marker::PhantomData,
171 num::{NonZeroU32, NonZeroU64, NonZeroUsize},
172};
173
174type DkgCompletion<V, P, D> = Box<dyn FnOnce(Option<EpochInfo<V, P, D>>) + Send>;
175
176mod dealing;
177mod dkg;
178mod follower;
179mod inclusion;
180mod setup;
181#[cfg(test)]
182mod utils;
183use setup::{Setup, StateSyncStart};
184
185pub(crate) struct DkgConfig<V, P, D>
187where
188 V: BlsVariant,
189 P: PublicKey,
190 D: Directory<P>,
191{
192 pub(crate) participants: Set<P>,
193 pub(crate) directory: D,
197 pub(crate) completion: DkgCompletion<V, P, D>,
198}
199
200enum Mode<V, P, D>
201where
202 V: BlsVariant,
203 P: PublicKey,
204 D: Directory<P>,
205{
206 Reshare,
207 Dkg {
208 participants: Set<P>,
209 directory: D,
210 completion: Option<DkgCompletion<V, P, D>>,
211 },
212}
213
214pub struct Config<C, M, X, P, SS, T, BV, S, MV, R>
216where
217 C: Signer,
218 X: Blocker<PublicKey = C::PublicKey>,
219 S: Scheme + SimplexScheme<MV::Commitment, PublicKey = C::PublicKey>,
220 MV: MarshalVariant,
221 MV::ApplicationBlock: ReshareBlock,
222 <MV::ApplicationBlock as ReshareBlock>::Signer: Signer<PublicKey = C::PublicKey>,
223 R: Registrar<PublicKey = C::PublicKey>,
224{
225 pub signer: C,
227
228 pub manager: M,
233
234 pub blocker: X,
236
237 pub participants_provider: P,
239
240 pub secret_store: SS,
242
243 pub strategy: T,
245
246 pub registrar: R,
248
249 pub marshal: MarshalMailbox<S, MV>,
252
253 pub state_sync: StateSyncPlan<
255 S,
256 MV::Commitment,
257 R::Variant,
258 <MV::ApplicationBlock as ReshareBlock>::Directory,
259 >,
260
261 pub fence: Fence,
263
264 pub namespace: &'static [u8],
266
267 pub sharing_mode: SharingMode,
269
270 pub reveal: Reveal,
272
273 pub mailbox_size: NonZeroUsize,
275
276 pub partition_prefix: String,
278
279 pub max_participants: NonZeroU32,
282
283 pub blocks_per_epoch: NonZeroU64,
285
286 pub batch_verifier: PhantomData<BV>,
288}
289
290pub struct Actor<E, B, V, C, M, X, P, SS, T, BV, S, MV, R, A = Exact>
291where
292 E: Spawner + CryptoRng + Metrics + BufferPooler + Clock + Storage,
293 B: ReshareBlock<Variant = V, Signer = C>,
294 V: BlsVariant,
295 C: Signer,
296 M: Manager<PublicKey = C::PublicKey, Directory = B::Directory>,
297 X: Blocker<PublicKey = C::PublicKey>,
298 P: ParticipantsProvider<PublicKey = C::PublicKey, Directory = B::Directory>,
299 SS: SecretStore,
300 T: Strategy,
301 BV: BatchVerifier<PublicKey = C::PublicKey> + Send + 'static,
302 S: Scheme + SimplexScheme<MV::Commitment, PublicKey = C::PublicKey>,
303 MV: MarshalVariant<ApplicationBlock = B>,
304 R: Registrar<Variant = V, PublicKey = C::PublicKey>,
305 A: Acknowledgement,
306{
307 context: ContextCell<E>,
308 mailbox: MailboxReceiver<Message<B, V, C, A>>,
309 signer: C,
310 manager: M,
311 blocker: X,
312 participants_provider: P,
313 secret_store: Option<SS>,
314 strategy: T,
315 registrar: R,
316 marshal: MarshalMailbox<S, MV>,
317 state_sync: StateSyncPlan<S, MV::Commitment, V, B::Directory>,
318 fence: Fence,
319 namespace: &'static [u8],
320 sharing_mode: SharingMode,
321 reveal: Reveal,
322 partition_prefix: String,
323 max_participants: NonZeroU32,
324 blocks_per_epoch: NonZeroU64,
325 epocher: FixedEpocher,
326 metrics: ReshareMetrics<C::PublicKey>,
327 mode: Mode<V, C::PublicKey, B::Directory>,
328 batch_verifier: PhantomData<BV>,
329}
330
331impl<E, B, V, C, M, X, P, SS, T, BV, S, MV, R, A> Actor<E, B, V, C, M, X, P, SS, T, BV, S, MV, R, A>
332where
333 E: Spawner + CryptoRng + Metrics + BufferPooler + Clock + Storage,
334 B: ReshareBlock<Variant = V, Signer = C>,
335 V: BlsVariant,
336 C: Signer,
337 M: Manager<PublicKey = C::PublicKey, Directory = B::Directory>,
338 X: Blocker<PublicKey = C::PublicKey>,
339 P: ParticipantsProvider<PublicKey = C::PublicKey, Directory = B::Directory>,
340 SS: SecretStore,
341 T: Strategy,
342 BV: BatchVerifier<PublicKey = C::PublicKey> + Send + 'static,
343 S: Scheme + SimplexScheme<MV::Commitment, PublicKey = C::PublicKey>,
344 MV: MarshalVariant<ApplicationBlock = B>,
345 R: Registrar<Variant = V, PublicKey = C::PublicKey>,
346 A: Acknowledgement,
347{
348 pub fn new(
349 context: E,
350 config: Config<C, M, X, P, SS, T, BV, S, MV, R>,
351 ) -> (Self, Mailbox<B, V, C, A>) {
352 let epocher = FixedEpocher::new(config.blocks_per_epoch);
353 let (sender, mailbox) = actor_mailbox::new(context.child("mailbox"), config.mailbox_size);
354 let metrics = ReshareMetrics::new(&context);
355 (
356 Self {
357 context: ContextCell::new(context),
358 mailbox,
359 signer: config.signer,
360 manager: config.manager,
361 blocker: config.blocker,
362 participants_provider: config.participants_provider,
363 secret_store: Some(config.secret_store),
364 strategy: config.strategy,
365 registrar: config.registrar,
366 marshal: config.marshal,
367 state_sync: config.state_sync,
368 fence: config.fence,
369 namespace: config.namespace,
370 sharing_mode: config.sharing_mode,
371 reveal: config.reveal,
372 partition_prefix: config.partition_prefix,
373 max_participants: config.max_participants,
374 blocks_per_epoch: config.blocks_per_epoch,
375 epocher,
376 metrics,
377 mode: Mode::Reshare,
378 batch_verifier: config.batch_verifier,
379 },
380 Mailbox::new(sender),
381 )
382 }
383
384 pub(crate) fn new_dkg(
385 context: E,
386 config: Config<C, M, X, P, SS, T, BV, S, MV, R>,
387 dkg: DkgConfig<V, C::PublicKey, B::Directory>,
388 ) -> (Self, Mailbox<B, V, C, A>) {
389 let (mut actor, mailbox) = Self::new(context, config);
390 actor.mode = Mode::Dkg {
391 participants: dkg.participants,
392 directory: dkg.directory,
393 completion: Some(dkg.completion),
394 };
395 (actor, mailbox)
396 }
397
398 pub fn start<SE, RE>(mut self, chan: (SE, RE)) -> Handle<()>
399 where
400 SE: Sender<PublicKey = C::PublicKey>,
401 RE: Receiver<PublicKey = C::PublicKey>,
402 {
403 spawn_cell!(self.context, self.run(chan))
404 }
405
406 async fn run<SE, RE>(mut self, (sender, receiver): (SE, RE))
407 where
408 SE: Sender<PublicKey = C::PublicKey>,
409 RE: Receiver<PublicKey = C::PublicKey>,
410 {
411 let secret_store = self
412 .secret_store
413 .take()
414 .expect("secret store must be available when actor starts");
415 let mut store = Store::init(
416 self.context.child("store"),
417 &self.partition_prefix,
418 self.max_participants,
419 secret_store,
420 )
421 .await;
422
423 let (mux, mut dealing_mux) = Muxer::new(self.context.child("mux"), sender, receiver, 128);
424 mux.start();
425
426 let recovered_epoch = state_sync::recovered_epoch(&self.marshal, &self.epocher).await;
427 let state_sync = self
428 .state_sync
429 .resolve(
430 self.context.as_present().child("state_sync"),
431 recovered_epoch,
432 )
433 .await;
434
435 let mut state_sync = if let Some(state_sync) = state_sync {
440 let share = self.recovered_share(&mut store, &state_sync.info).await;
441 self.register_epoch(&state_sync.info, share).await;
442 let floor = self
443 .marshal
444 .subscribe_by_commitment(
445 state_sync.floor.proposal.payload,
446 CommitmentFallback::Wait,
447 )
448 .await
449 .expect("marshal must yield state sync floor block");
450 Some(StateSyncStart {
451 info: state_sync.info,
452 floor: floor.height(),
453 })
454 } else {
455 None
456 };
457
458 if matches!(self.mode, Mode::Dkg { .. }) {
459 self.run_dkg(&mut store, &mut dealing_mux).await;
460 return;
461 }
462
463 let mut current_epoch = state_sync.as_ref().map(|start| start.info.epoch);
464 loop {
465 let Some(prepared) = self
466 .setup(&mut store, current_epoch.take(), state_sync.take())
467 .await
468 else {
469 return;
470 };
471 let Setup::Participate(prepared) = prepared else {
472 if self.follow(&mut store).await.is_break() {
473 return;
474 }
475 current_epoch = store.current().map(|info| info.epoch);
476 continue;
477 };
478 let mut prepared = *prepared;
479
480 let chan = dealing_mux
481 .register(prepared.epoch.get())
482 .await
483 .expect("failed to register reshare epoch channel");
484
485 if prepared.phase == EpochPhase::Early {
486 let dealer = prepared.dealer.as_mut();
487 let player = prepared.player.as_mut();
488 if self
489 .dealing(prepared.epoch, &mut store, dealer, player, chan)
490 .await
491 .is_break()
492 {
493 return;
494 }
495 }
496
497 if self
498 .inclusion(
499 prepared.epoch,
500 &prepared.info,
501 &mut store,
502 prepared.dealer.as_mut(),
503 )
504 .await
505 .is_break()
506 {
507 return;
508 }
509 current_epoch = Some(prepared.epoch.next());
510 }
511 }
512}