mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 20:29:23 +00:00
Remove the RemoveParticipant protocol for having new DKGs specify the participants which were removed
Obvious code cleanup is obvious.
This commit is contained in:
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user