Remove the RemoveParticipant protocol for having new DKGs specify the participants which were removed

Obvious code cleanup is obvious.
This commit is contained in:
Luke Parker
2023-12-14 23:45:15 -05:00
parent b60e3c2524
commit 2532423d42
17 changed files with 144 additions and 615 deletions

View File

@@ -1,9 +1,8 @@
/*
A MuSig-based signing protocol executed with the validators' keys.
This is used for confirming the results of a DKG on-chain, an operation requiring all validators,
and for removing another validator before the DKG completes, an operation requiring a
supermajority of validators.
This is used for confirming the results of a DKG on-chain, an operation requiring all validators
which aren't specified as removed while still satisfying a supermajority.
Since we're using the validator's keys, as needed for their being the root of trust, the
coordinator must perform the signing. This is distinct from all other group-signing operations,
@@ -65,7 +64,7 @@ use rand_core::OsRng;
use blake2::{Digest, Blake2s256};
use ciphersuite::{
group::{ff::PrimeField, Group, GroupEncoding},
group::{ff::PrimeField, GroupEncoding},
Ciphersuite, Ristretto,
};
use frost::{
@@ -79,10 +78,8 @@ use frost_schnorrkel::Schnorrkel;
use scale::Encode;
use serai_client::{
Public, SeraiAddress,
validator_sets::primitives::{
KeyPair, musig_context, set_keys_message, remove_participant_message,
},
Public,
validator_sets::primitives::{KeyPair, musig_context, set_keys_message},
};
use serai_db::*;
@@ -212,15 +209,11 @@ impl<T: DbTxn, C: Encode> SigningProtocol<'_, T, C> {
// Get the keys of the participants, noted by their threshold is, and return a new map indexed by
// the MuSig is.
//
// If sort_by_keys = true, the MuSig is will index the keys once sorted. Else, the MuSig is will
// index the validators in the order they've been defined.
fn threshold_i_map_to_keys_and_musig_i_map(
spec: &TributarySpec,
removed: &[<Ristretto as Ciphersuite>::G],
our_key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
mut map: HashMap<Participant, Vec<u8>>,
sort_by_keys: bool,
) -> (Vec<<Ristretto as Ciphersuite>::G>, HashMap<Participant, Vec<u8>>) {
// Insert our own index so calculations aren't offset
let our_threshold_i =
@@ -243,10 +236,6 @@ fn threshold_i_map_to_keys_and_musig_i_map(
for threshold_i in threshold_is {
sorted.push((key_from_threshold_i(threshold_i), map.remove(&threshold_i).unwrap()));
}
if sort_by_keys {
// Substrate expects these signers to be sorted by key
sorted.sort_by(|(key1, _), (key2, _)| key1.to_bytes().cmp(&key2.to_bytes()));
}
// Now that signers are sorted, with their shares, create a map with the is needed for MuSig
let mut participants = vec![];
@@ -302,15 +291,13 @@ impl<T: DbTxn> DkgConfirmer<'_, T> {
key_pair: &KeyPair,
) -> Result<(AlgorithmSignatureMachine<Ristretto, Schnorrkel>, [u8; 32]), Participant> {
let participants = self.spec.validators().iter().map(|val| val.0).collect::<Vec<_>>();
let preprocesses = threshold_i_map_to_keys_and_musig_i_map(
self.spec,
&self.removed,
self.key,
preprocesses,
false,
)
.1;
let msg = set_keys_message(&self.spec.set(), key_pair);
let preprocesses =
threshold_i_map_to_keys_and_musig_i_map(self.spec, &self.removed, self.key, preprocesses).1;
let msg = set_keys_message(
&self.spec.set(),
&self.removed.iter().map(|key| Public(key.to_bytes())).collect::<Vec<_>>(),
key_pair,
);
self.signing_protocol().share_internal(&participants, preprocesses, &msg)
}
// Get the share for this confirmation, if the preprocesses are valid.
@@ -329,7 +316,7 @@ impl<T: DbTxn> DkgConfirmer<'_, T> {
shares: HashMap<Participant, Vec<u8>>,
) -> Result<[u8; 64], Participant> {
let shares =
threshold_i_map_to_keys_and_musig_i_map(self.spec, &self.removed, self.key, shares, false).1;
threshold_i_map_to_keys_and_musig_i_map(self.spec, &self.removed, self.key, shares).1;
let machine = self
.share_internal(preprocesses, key_pair)
@@ -339,83 +326,3 @@ impl<T: DbTxn> DkgConfirmer<'_, T> {
self.signing_protocol().complete_internal(machine, shares)
}
}
pub(crate) struct DkgRemoval<'a, T: DbTxn> {
pub(crate) key: &'a Zeroizing<<Ristretto as Ciphersuite>::F>,
pub(crate) spec: &'a TributarySpec,
pub(crate) removed: &'a [<Ristretto as Ciphersuite>::G],
pub(crate) txn: &'a mut T,
pub(crate) removing: [u8; 32],
pub(crate) attempt: u32,
}
impl<T: DbTxn> DkgRemoval<'_, T> {
fn signing_protocol(&mut self) -> SigningProtocol<'_, T, (&'static [u8; 10], [u8; 32], u32)> {
let context = (b"DkgRemoval", self.removing, self.attempt);
SigningProtocol { key: self.key, spec: self.spec, txn: self.txn, context }
}
fn preprocess_internal(
&mut self,
participants: Option<&[<Ristretto as Ciphersuite>::G]>,
) -> (AlgorithmSignMachine<Ristretto, Schnorrkel>, [u8; 64]) {
// We won't know the participants when we first preprocess
// If we don't, we use our key alone as the participant
let just_us = [<Ristretto as Ciphersuite>::G::generator() * self.key.deref()];
let to_musig = if let Some(participants) = participants { participants } else { &just_us };
let (machine, preprocess) = self.signing_protocol().preprocess_internal(to_musig);
// If we're now specifying participants, confirm the commitments were the same
if participants.is_some() {
let (_, theoretical_preprocess) = self.signing_protocol().preprocess_internal(&just_us);
assert_eq!(theoretical_preprocess, preprocess);
}
(machine, preprocess)
}
// Get the preprocess for this confirmation.
pub(crate) fn preprocess(&mut self) -> [u8; 64] {
self.preprocess_internal(None).1
}
fn share_internal(
&mut self,
preprocesses: HashMap<Participant, Vec<u8>>,
) -> Result<(AlgorithmSignatureMachine<Ristretto, Schnorrkel>, [u8; 32]), Participant> {
let (participants, preprocesses) = threshold_i_map_to_keys_and_musig_i_map(
self.spec,
self.removed,
self.key,
preprocesses,
true,
);
let msg = remove_participant_message(&self.spec.set(), Public(self.removing));
self.signing_protocol().share_internal(&participants, preprocesses, &msg)
}
// Get the share for this confirmation, if the preprocesses are valid.
pub(crate) fn share(
&mut self,
preprocesses: HashMap<Participant, Vec<u8>>,
) -> Result<[u8; 32], Participant> {
self.share_internal(preprocesses).map(|(_, share)| share)
}
pub(crate) fn complete(
&mut self,
preprocesses: HashMap<Participant, Vec<u8>>,
shares: HashMap<Participant, Vec<u8>>,
) -> Result<(Vec<SeraiAddress>, [u8; 64]), Participant> {
let (participants, shares) =
threshold_i_map_to_keys_and_musig_i_map(self.spec, self.removed, self.key, shares, true);
let signers = participants.iter().map(|key| SeraiAddress(key.to_bytes())).collect::<Vec<_>>();
let machine = self
.share_internal(preprocesses)
.expect("trying to complete a machine which failed to preprocess")
.0;
let signature = self.signing_protocol().complete_internal(machine, shares)?;
Ok((signers, signature))
}
}