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:
Luke Parker
2024-08-04 01:14:30 -04:00
parent 5ed355902b
commit 58a435d4e9
8 changed files with 33 additions and 53 deletions

View File

@@ -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",

View File

@@ -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 {

View File

@@ -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 }

View File

@@ -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,
}))
}

View File

@@ -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,
}
}

View File

@@ -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",

View File

@@ -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)?;
}

View File

@@ -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> {