From 58a435d4e986adb78c4eb2057cb7a62ab44bc6fe Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sun, 4 Aug 2024 01:14:30 -0400 Subject: [PATCH] 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. --- substrate/abi/Cargo.toml | 8 +++- substrate/abi/src/validator_sets.rs | 2 +- substrate/client/Cargo.toml | 2 + substrate/client/src/serai/validator_sets.rs | 7 +--- substrate/runtime/src/abi.rs | 14 ++----- substrate/validator-sets/pallet/Cargo.toml | 7 +++- substrate/validator-sets/pallet/src/lib.rs | 38 ++++++------------- .../validator-sets/primitives/src/lib.rs | 8 +--- 8 files changed, 33 insertions(+), 53 deletions(-) diff --git a/substrate/abi/Cargo.toml b/substrate/abi/Cargo.toml index c2947aaa..71024f88 100644 --- a/substrate/abi/Cargo.toml +++ b/substrate/abi/Cargo.toml @@ -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", diff --git a/substrate/abi/src/validator_sets.rs b/substrate/abi/src/validator_sets.rs index 4552cb8d..7a7bdc00 100644 --- a/substrate/abi/src/validator_sets.rs +++ b/substrate/abi/src/validator_sets.rs @@ -11,8 +11,8 @@ use serai_validator_sets_primitives::*; pub enum Call { set_keys { network: NetworkId, - removed_participants: BoundedVec>, key_pair: KeyPair, + signature_participants: bitvec::vec::BitVec, signature: Signature, }, set_embedded_elliptic_curve_key { diff --git a/substrate/client/Cargo.toml b/substrate/client/Cargo.toml index 7bb252ce..b848b57d 100644 --- a/substrate/client/Cargo.toml +++ b/substrate/client/Cargo.toml @@ -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 } diff --git a/substrate/client/src/serai/validator_sets.rs b/substrate/client/src/serai/validator_sets.rs index c3d881bd..87ccde46 100644 --- a/substrate/client/src/serai/validator_sets.rs +++ b/substrate/client/src/serai/validator_sets.rs @@ -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, signature: Signature, ) -> Transaction { Serai::unsigned(serai_abi::Call::ValidatorSets(serai_abi::validator_sets::Call::set_keys { network, - removed_participants, key_pair, + signature_participants, signature, })) } diff --git a/substrate/runtime/src/abi.rs b/substrate/runtime/src/abi.rs index 6f8917ae..aafa17fd 100644 --- a/substrate/runtime/src/abi.rs +++ b/substrate/runtime/src/abi.rs @@ -103,16 +103,13 @@ impl From 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::>(), - ) - .unwrap(), key_pair, + signature_participants, signature, }), serai_abi::validator_sets::Call::set_embedded_elliptic_curve_key { @@ -289,14 +286,11 @@ impl TryInto 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::>(), - ) - .unwrap(), key_pair, + signature_participants, signature, } } diff --git a/substrate/validator-sets/pallet/Cargo.toml b/substrate/validator-sets/pallet/Cargo.toml index aff27e3e..445544f5 100644 --- a/substrate/validator-sets/pallet/Cargo.toml +++ b/substrate/validator-sets/pallet/Cargo.toml @@ -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", diff --git a/substrate/validator-sets/pallet/src/lib.rs b/substrate/validator-sets/pallet/src/lib.rs index d7917ff6..4ceda9fa 100644 --- a/substrate/validator-sets/pallet/src/lib.rs +++ b/substrate/validator-sets/pallet/src/lib.rs @@ -927,14 +927,15 @@ pub mod pallet { pub fn set_keys( origin: OriginFor, network: NetworkId, - removed_participants: BoundedVec>, key_pair: KeyPair, + signature_participants: bitvec::vec::BitVec, 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::::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::::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::::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)?; } diff --git a/substrate/validator-sets/primitives/src/lib.rs b/substrate/validator-sets/primitives/src/lib.rs index c900b0a9..90d58c37 100644 --- a/substrate/validator-sets/primitives/src/lib.rs +++ b/substrate/validator-sets/primitives/src/lib.rs @@ -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 { - (b"ValidatorSets-set_keys", set, removed_participants, key_pair).encode() +pub fn set_keys_message(set: &ValidatorSet, key_pair: &KeyPair) -> Vec { + (b"ValidatorSets-set_keys", set, key_pair).encode() } pub fn report_slashes_message(set: &ValidatorSet, slashes: &[(Public, u32)]) -> Vec {