mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
* Update the coordinator to give key shares based on weight, not based on existence Participants are now identified by their starting index. While this compiles, the following is unimplemented: 1) A conversion for DKG `i` values. It assumes the threshold `i` values used will be identical for the MuSig signature used to confirm the DKG. 2) Expansion from compressed values to full values before forwarding to the processor. * Add a fn to the DkgConfirmer to convert `i` values as needed Also removes TODOs regarding Serai ensuring validator key uniqueness + validity. The current infra achieves both. * Have the Tributary DB track participation by shares, not by count * Prevent a node from obtaining 34% of the maximum amount of key shares This is actually mainly intended to set a bound on message sizes in the coordinator. Message sizes are amplified by the amount of key shares held, so setting an upper bound on said amount lets it determine constants. While that upper bound could be 150, that'd be unreasonable and increase the potential for DoS attacks. * Correct the mechanism to detect if sufficient accumulation has occured It used to check if the latest accumulation hit the required threshold. Now, accumulations may jump past the required threshold. The required mechanism is to check the threshold wasn't prior met and is now met. * Finish updating the coordinator to handle a multiple key share per validator environment * Adjust stategy re: preventing noce reuse in DKG Confirmer * Add TODOs regarding dropped transactions, add possible TODO fix * Update tests/coordinator This doesn't add new multi-key-share tests, it solely updates the existing single key-share tests to compile and run, with the necessary fixes to the coordinator. * Update processor key_gen to handle generating multiple key shares at once * Update SubstrateSigner * Update signer, clippy * Update processor tests * Update processor docker tests
123 lines
3.2 KiB
Rust
123 lines
3.2 KiB
Rust
#![cfg_attr(not(feature = "std"), no_std)]
|
|
|
|
#[cfg(feature = "std")]
|
|
use zeroize::Zeroize;
|
|
|
|
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
|
|
|
use scale::{Encode, Decode, MaxEncodedLen};
|
|
use scale_info::TypeInfo;
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
use sp_core::{ConstU32, sr25519::Public, bounded::BoundedVec};
|
|
#[cfg(not(feature = "std"))]
|
|
use sp_std::vec::Vec;
|
|
|
|
use serai_primitives::NetworkId;
|
|
|
|
/// The maximum amount of key shares per set.
|
|
pub const MAX_KEY_SHARES_PER_SET: u32 = 150;
|
|
// Support keys up to 96 bytes (BLS12-381 G2).
|
|
pub const MAX_KEY_LEN: u32 = 96;
|
|
|
|
/// The type used to identify a specific session of validators.
|
|
#[derive(
|
|
Clone,
|
|
Copy,
|
|
PartialEq,
|
|
Eq,
|
|
Hash,
|
|
Debug,
|
|
Serialize,
|
|
Deserialize,
|
|
Encode,
|
|
Decode,
|
|
TypeInfo,
|
|
MaxEncodedLen,
|
|
Default,
|
|
)]
|
|
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
pub struct Session(pub u32);
|
|
|
|
/// The type used to identify a specific validator set during a specific session.
|
|
#[derive(
|
|
Clone,
|
|
Copy,
|
|
PartialEq,
|
|
Eq,
|
|
Hash,
|
|
Debug,
|
|
Serialize,
|
|
Deserialize,
|
|
Encode,
|
|
Decode,
|
|
TypeInfo,
|
|
MaxEncodedLen,
|
|
)]
|
|
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
pub struct ValidatorSet {
|
|
pub session: Session,
|
|
pub network: NetworkId,
|
|
}
|
|
|
|
type MaxKeyLen = ConstU32<MAX_KEY_LEN>;
|
|
/// The type representing a Key from an external network.
|
|
pub type ExternalKey = BoundedVec<u8, MaxKeyLen>;
|
|
|
|
/// The key pair for a validator set.
|
|
///
|
|
/// This is their Ristretto key, used for signing Batches, and their key on the external network.
|
|
pub type KeyPair = (Public, ExternalKey);
|
|
|
|
/// The MuSig context for a validator set.
|
|
pub fn musig_context(set: ValidatorSet) -> Vec<u8> {
|
|
[b"ValidatorSets-musig_key".as_ref(), &set.encode()].concat()
|
|
}
|
|
|
|
/// The MuSig public key for a validator set.
|
|
///
|
|
/// This function panics on invalid input.
|
|
pub fn musig_key(set: ValidatorSet, set_keys: &[Public]) -> Public {
|
|
let mut keys = Vec::new();
|
|
for key in set_keys {
|
|
keys.push(
|
|
<Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut key.0.as_ref())
|
|
.expect("invalid participant"),
|
|
);
|
|
}
|
|
Public(dkg::musig::musig_key::<Ristretto>(&musig_context(set), &keys).unwrap().to_bytes())
|
|
}
|
|
|
|
/// The message for the set_keys signature.
|
|
pub fn set_keys_message(set: &ValidatorSet, key_pair: &KeyPair) -> Vec<u8> {
|
|
[b"ValidatorSets-key_pair".as_ref(), &(set, key_pair).encode()].concat()
|
|
}
|
|
|
|
/// For a set of validators whose key shares may exceed the maximum, reduce until they equal the
|
|
/// maximum.
|
|
///
|
|
/// Reduction occurs by reducing each validator in a reverse round-robin.
|
|
pub fn amortize_excess_key_shares(validators: &mut [(Public, u16)]) {
|
|
let total_key_shares = validators.iter().map(|(_, shares)| shares).sum::<u16>();
|
|
for i in 0 .. usize::try_from(
|
|
total_key_shares.saturating_sub(u16::try_from(MAX_KEY_SHARES_PER_SET).unwrap()),
|
|
)
|
|
.unwrap()
|
|
{
|
|
validators[validators.len() - ((i % validators.len()) + 1)].1 -= 1;
|
|
}
|
|
}
|
|
|
|
/// Returns the post-amortization key shares for the top validator.
|
|
///
|
|
/// Panics when `validators == 0`.
|
|
pub fn post_amortization_key_shares_for_top_validator(
|
|
validators: usize,
|
|
top: u64,
|
|
key_shares: u64,
|
|
) -> u64 {
|
|
top -
|
|
(key_shares.saturating_sub(MAX_KEY_SHARES_PER_SET.into()) /
|
|
u64::try_from(validators).unwrap())
|
|
}
|