mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-13 14:39:25 +00:00
Have set_keys take signature_participants, not removed_participants
Now no one is removed from the DKG. Only `t` people publish the key however. Uses a BitVec for an efficient encoding of the participants.
This commit is contained in:
@@ -16,8 +16,10 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
||||
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "bit-vec"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive", "bit-vec"] }
|
||||
|
||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
||||
@@ -39,6 +41,8 @@ serai-signals-primitives = { path = "../signals/primitives", version = "0.1", de
|
||||
|
||||
[features]
|
||||
std = [
|
||||
"bitvec/std",
|
||||
|
||||
"scale/std",
|
||||
"scale-info/std",
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ use serai_validator_sets_primitives::*;
|
||||
pub enum Call {
|
||||
set_keys {
|
||||
network: NetworkId,
|
||||
removed_participants: BoundedVec<SeraiAddress, ConstU32<{ MAX_KEY_SHARES_PER_SET / 3 }>>,
|
||||
key_pair: KeyPair,
|
||||
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||
signature: Signature,
|
||||
},
|
||||
set_embedded_elliptic_curve_key {
|
||||
|
||||
@@ -20,6 +20,8 @@ workspace = true
|
||||
zeroize = "^1.5"
|
||||
thiserror = { version = "1", optional = true }
|
||||
|
||||
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
||||
|
||||
hex = "0.4"
|
||||
scale = { package = "parity-scale-codec", version = "3" }
|
||||
serde = { version = "1", features = ["derive"], optional = true }
|
||||
|
||||
@@ -181,17 +181,14 @@ impl<'a> SeraiValidatorSets<'a> {
|
||||
|
||||
pub fn set_keys(
|
||||
network: NetworkId,
|
||||
removed_participants: sp_runtime::BoundedVec<
|
||||
SeraiAddress,
|
||||
sp_core::ConstU32<{ primitives::MAX_KEY_SHARES_PER_SET / 3 }>,
|
||||
>,
|
||||
key_pair: KeyPair,
|
||||
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||
signature: Signature,
|
||||
) -> Transaction {
|
||||
Serai::unsigned(serai_abi::Call::ValidatorSets(serai_abi::validator_sets::Call::set_keys {
|
||||
network,
|
||||
removed_participants,
|
||||
key_pair,
|
||||
signature_participants,
|
||||
signature,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -103,16 +103,13 @@ impl From<Call> for RuntimeCall {
|
||||
Call::ValidatorSets(vs) => match vs {
|
||||
serai_abi::validator_sets::Call::set_keys {
|
||||
network,
|
||||
removed_participants,
|
||||
key_pair,
|
||||
signature_participants,
|
||||
signature,
|
||||
} => RuntimeCall::ValidatorSets(validator_sets::Call::set_keys {
|
||||
network,
|
||||
removed_participants: <_>::try_from(
|
||||
removed_participants.into_iter().map(PublicKey::from).collect::<Vec<_>>(),
|
||||
)
|
||||
.unwrap(),
|
||||
key_pair,
|
||||
signature_participants,
|
||||
signature,
|
||||
}),
|
||||
serai_abi::validator_sets::Call::set_embedded_elliptic_curve_key {
|
||||
@@ -289,14 +286,11 @@ impl TryInto<Call> for RuntimeCall {
|
||||
_ => Err(())?,
|
||||
}),
|
||||
RuntimeCall::ValidatorSets(call) => Call::ValidatorSets(match call {
|
||||
validator_sets::Call::set_keys { network, removed_participants, key_pair, signature } => {
|
||||
validator_sets::Call::set_keys { network, key_pair, signature_participants, signature } => {
|
||||
serai_abi::validator_sets::Call::set_keys {
|
||||
network,
|
||||
removed_participants: <_>::try_from(
|
||||
removed_participants.into_iter().map(SeraiAddress::from).collect::<Vec<_>>(),
|
||||
)
|
||||
.unwrap(),
|
||||
key_pair,
|
||||
signature_participants,
|
||||
signature,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ ignored = ["scale", "scale-info"]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
||||
hashbrown = { version = "0.14", default-features = false, features = ["ahash", "inline-more"] }
|
||||
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "bit-vec"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive", "bit-vec"] }
|
||||
|
||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
|
||||
|
||||
@@ -48,6 +49,8 @@ dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-
|
||||
|
||||
[features]
|
||||
std = [
|
||||
"bitvec/std",
|
||||
|
||||
"scale/std",
|
||||
"scale-info/std",
|
||||
|
||||
|
||||
@@ -927,14 +927,15 @@ pub mod pallet {
|
||||
pub fn set_keys(
|
||||
origin: OriginFor<T>,
|
||||
network: NetworkId,
|
||||
removed_participants: BoundedVec<Public, ConstU32<{ MAX_KEY_SHARES_PER_SET / 3 }>>,
|
||||
key_pair: KeyPair,
|
||||
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||
signature: Signature,
|
||||
) -> DispatchResult {
|
||||
ensure_none(origin)?;
|
||||
|
||||
// signature isn't checked as this is an unsigned transaction, and validate_unsigned
|
||||
// (called by pre_dispatch) checks it
|
||||
let _ = signature_participants;
|
||||
let _ = signature;
|
||||
|
||||
let session = Self::session(network).unwrap();
|
||||
@@ -949,15 +950,6 @@ pub mod pallet {
|
||||
Self::set_total_allocated_stake(network);
|
||||
}
|
||||
|
||||
// This does not remove from TotalAllocatedStake or InSet in order to:
|
||||
// 1) Not decrease the stake present in this set. This means removed participants are
|
||||
// still liable for the economic security of the external network. This prevents
|
||||
// a decided set, which is economically secure, from falling below the threshold.
|
||||
// 2) Not allow parties removed to immediately deallocate, per commentary on deallocation
|
||||
// scheduling (https://github.com/serai-dex/serai/issues/394).
|
||||
for removed in removed_participants {
|
||||
Self::deposit_event(Event::ParticipantRemoved { set, removed });
|
||||
}
|
||||
Self::deposit_event(Event::KeyGen { set, key_pair });
|
||||
|
||||
Ok(())
|
||||
@@ -1070,7 +1062,7 @@ pub mod pallet {
|
||||
fn validate_unsigned(_: TransactionSource, call: &Self::Call) -> TransactionValidity {
|
||||
// Match to be exhaustive
|
||||
match call {
|
||||
Call::set_keys { network, ref removed_participants, ref key_pair, ref signature } => {
|
||||
Call::set_keys { network, ref key_pair, ref signature_participants, ref signature } => {
|
||||
let network = *network;
|
||||
|
||||
// Don't allow the Serai set to set_keys, as they have no reason to do so
|
||||
@@ -1094,30 +1086,24 @@ pub mod pallet {
|
||||
// session on this assumption
|
||||
assert_eq!(Pallet::<T>::latest_decided_session(network), Some(current_session));
|
||||
|
||||
// This does not slash the removed participants as that'll be done at the end of the
|
||||
// set's lifetime
|
||||
let mut removed = hashbrown::HashSet::new();
|
||||
for participant in removed_participants {
|
||||
// Confirm this wasn't duplicated
|
||||
if removed.contains(&participant.0) {
|
||||
Err(InvalidTransaction::Custom(2))?;
|
||||
}
|
||||
removed.insert(participant.0);
|
||||
}
|
||||
|
||||
let participants =
|
||||
Participants::<T>::get(network).expect("session existed without participants");
|
||||
|
||||
// Check the bitvec is of the proper length
|
||||
if participants.len() != signature_participants.len() {
|
||||
Err(InvalidTransaction::Custom(2))?;
|
||||
}
|
||||
|
||||
let mut all_key_shares = 0;
|
||||
let mut signers = vec![];
|
||||
let mut signing_key_shares = 0;
|
||||
for participant in participants {
|
||||
for (participant, in_use) in participants.into_iter().zip(signature_participants) {
|
||||
let participant = participant.0;
|
||||
let shares = InSet::<T>::get(network, participant)
|
||||
.expect("participant from Participants wasn't InSet");
|
||||
all_key_shares += shares;
|
||||
|
||||
if removed.contains(&participant.0) {
|
||||
if !in_use {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1135,9 +1121,7 @@ pub mod pallet {
|
||||
// Verify the signature with the MuSig key of the signers
|
||||
// We theoretically don't need set_keys_message to bind to removed_participants, as the
|
||||
// key we're signing with effectively already does so, yet there's no reason not to
|
||||
if !musig_key(set, &signers)
|
||||
.verify(&set_keys_message(&set, removed_participants, key_pair), signature)
|
||||
{
|
||||
if !musig_key(set, &signers).verify(&set_keys_message(&set, key_pair), signature) {
|
||||
Err(InvalidTransaction::BadProof)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -99,12 +99,8 @@ pub fn musig_key(set: ValidatorSet, set_keys: &[Public]) -> Public {
|
||||
}
|
||||
|
||||
/// The message for the set_keys signature.
|
||||
pub fn set_keys_message(
|
||||
set: &ValidatorSet,
|
||||
removed_participants: &[Public],
|
||||
key_pair: &KeyPair,
|
||||
) -> Vec<u8> {
|
||||
(b"ValidatorSets-set_keys", set, removed_participants, key_pair).encode()
|
||||
pub fn set_keys_message(set: &ValidatorSet, key_pair: &KeyPair) -> Vec<u8> {
|
||||
(b"ValidatorSets-set_keys", set, key_pair).encode()
|
||||
}
|
||||
|
||||
pub fn report_slashes_message(set: &ValidatorSet, slashes: &[(Public, u32)]) -> Vec<u8> {
|
||||
|
||||
Reference in New Issue
Block a user