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
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
||||||
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"] }
|
||||||
|
|
||||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
||||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], 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]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
|
"bitvec/std",
|
||||||
|
|
||||||
"scale/std",
|
"scale/std",
|
||||||
"scale-info/std",
|
"scale-info/std",
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ use serai_validator_sets_primitives::*;
|
|||||||
pub enum Call {
|
pub enum Call {
|
||||||
set_keys {
|
set_keys {
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
removed_participants: BoundedVec<SeraiAddress, ConstU32<{ MAX_KEY_SHARES_PER_SET / 3 }>>,
|
|
||||||
key_pair: KeyPair,
|
key_pair: KeyPair,
|
||||||
|
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
},
|
},
|
||||||
set_embedded_elliptic_curve_key {
|
set_embedded_elliptic_curve_key {
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ workspace = true
|
|||||||
zeroize = "^1.5"
|
zeroize = "^1.5"
|
||||||
thiserror = { version = "1", optional = true }
|
thiserror = { version = "1", optional = true }
|
||||||
|
|
||||||
|
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
||||||
|
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
scale = { package = "parity-scale-codec", version = "3" }
|
scale = { package = "parity-scale-codec", version = "3" }
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
|||||||
@@ -181,17 +181,14 @@ impl<'a> SeraiValidatorSets<'a> {
|
|||||||
|
|
||||||
pub fn set_keys(
|
pub fn set_keys(
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
removed_participants: sp_runtime::BoundedVec<
|
|
||||||
SeraiAddress,
|
|
||||||
sp_core::ConstU32<{ primitives::MAX_KEY_SHARES_PER_SET / 3 }>,
|
|
||||||
>,
|
|
||||||
key_pair: KeyPair,
|
key_pair: KeyPair,
|
||||||
|
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
) -> Transaction {
|
) -> Transaction {
|
||||||
Serai::unsigned(serai_abi::Call::ValidatorSets(serai_abi::validator_sets::Call::set_keys {
|
Serai::unsigned(serai_abi::Call::ValidatorSets(serai_abi::validator_sets::Call::set_keys {
|
||||||
network,
|
network,
|
||||||
removed_participants,
|
|
||||||
key_pair,
|
key_pair,
|
||||||
|
signature_participants,
|
||||||
signature,
|
signature,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,16 +103,13 @@ impl From<Call> for RuntimeCall {
|
|||||||
Call::ValidatorSets(vs) => match vs {
|
Call::ValidatorSets(vs) => match vs {
|
||||||
serai_abi::validator_sets::Call::set_keys {
|
serai_abi::validator_sets::Call::set_keys {
|
||||||
network,
|
network,
|
||||||
removed_participants,
|
|
||||||
key_pair,
|
key_pair,
|
||||||
|
signature_participants,
|
||||||
signature,
|
signature,
|
||||||
} => RuntimeCall::ValidatorSets(validator_sets::Call::set_keys {
|
} => RuntimeCall::ValidatorSets(validator_sets::Call::set_keys {
|
||||||
network,
|
network,
|
||||||
removed_participants: <_>::try_from(
|
|
||||||
removed_participants.into_iter().map(PublicKey::from).collect::<Vec<_>>(),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
key_pair,
|
key_pair,
|
||||||
|
signature_participants,
|
||||||
signature,
|
signature,
|
||||||
}),
|
}),
|
||||||
serai_abi::validator_sets::Call::set_embedded_elliptic_curve_key {
|
serai_abi::validator_sets::Call::set_embedded_elliptic_curve_key {
|
||||||
@@ -289,14 +286,11 @@ impl TryInto<Call> for RuntimeCall {
|
|||||||
_ => Err(())?,
|
_ => Err(())?,
|
||||||
}),
|
}),
|
||||||
RuntimeCall::ValidatorSets(call) => Call::ValidatorSets(match call {
|
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 {
|
serai_abi::validator_sets::Call::set_keys {
|
||||||
network,
|
network,
|
||||||
removed_participants: <_>::try_from(
|
|
||||||
removed_participants.into_iter().map(SeraiAddress::from).collect::<Vec<_>>(),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
key_pair,
|
key_pair,
|
||||||
|
signature_participants,
|
||||||
signature,
|
signature,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,11 @@ ignored = ["scale", "scale-info"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
||||||
hashbrown = { version = "0.14", default-features = false, features = ["ahash", "inline-more"] }
|
hashbrown = { version = "0.14", default-features = false, features = ["ahash", "inline-more"] }
|
||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", 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"] }
|
scale-info = { version = "2", default-features = false, features = ["derive", "bit-vec"] }
|
||||||
|
|
||||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
|
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
|
||||||
|
|
||||||
@@ -48,6 +49,8 @@ dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
|
"bitvec/std",
|
||||||
|
|
||||||
"scale/std",
|
"scale/std",
|
||||||
"scale-info/std",
|
"scale-info/std",
|
||||||
|
|
||||||
|
|||||||
@@ -927,14 +927,15 @@ pub mod pallet {
|
|||||||
pub fn set_keys(
|
pub fn set_keys(
|
||||||
origin: OriginFor<T>,
|
origin: OriginFor<T>,
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
removed_participants: BoundedVec<Public, ConstU32<{ MAX_KEY_SHARES_PER_SET / 3 }>>,
|
|
||||||
key_pair: KeyPair,
|
key_pair: KeyPair,
|
||||||
|
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
ensure_none(origin)?;
|
ensure_none(origin)?;
|
||||||
|
|
||||||
// signature isn't checked as this is an unsigned transaction, and validate_unsigned
|
// signature isn't checked as this is an unsigned transaction, and validate_unsigned
|
||||||
// (called by pre_dispatch) checks it
|
// (called by pre_dispatch) checks it
|
||||||
|
let _ = signature_participants;
|
||||||
let _ = signature;
|
let _ = signature;
|
||||||
|
|
||||||
let session = Self::session(network).unwrap();
|
let session = Self::session(network).unwrap();
|
||||||
@@ -949,15 +950,6 @@ pub mod pallet {
|
|||||||
Self::set_total_allocated_stake(network);
|
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 });
|
Self::deposit_event(Event::KeyGen { set, key_pair });
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1070,7 +1062,7 @@ pub mod pallet {
|
|||||||
fn validate_unsigned(_: TransactionSource, call: &Self::Call) -> TransactionValidity {
|
fn validate_unsigned(_: TransactionSource, call: &Self::Call) -> TransactionValidity {
|
||||||
// Match to be exhaustive
|
// Match to be exhaustive
|
||||||
match call {
|
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;
|
let network = *network;
|
||||||
|
|
||||||
// Don't allow the Serai set to set_keys, as they have no reason to do so
|
// 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
|
// session on this assumption
|
||||||
assert_eq!(Pallet::<T>::latest_decided_session(network), Some(current_session));
|
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 =
|
let participants =
|
||||||
Participants::<T>::get(network).expect("session existed without 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 all_key_shares = 0;
|
||||||
let mut signers = vec![];
|
let mut signers = vec![];
|
||||||
let mut signing_key_shares = 0;
|
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 participant = participant.0;
|
||||||
let shares = InSet::<T>::get(network, participant)
|
let shares = InSet::<T>::get(network, participant)
|
||||||
.expect("participant from Participants wasn't InSet");
|
.expect("participant from Participants wasn't InSet");
|
||||||
all_key_shares += shares;
|
all_key_shares += shares;
|
||||||
|
|
||||||
if removed.contains(&participant.0) {
|
if !in_use {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1135,9 +1121,7 @@ pub mod pallet {
|
|||||||
// Verify the signature with the MuSig key of the signers
|
// 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
|
// 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
|
// key we're signing with effectively already does so, yet there's no reason not to
|
||||||
if !musig_key(set, &signers)
|
if !musig_key(set, &signers).verify(&set_keys_message(&set, key_pair), signature) {
|
||||||
.verify(&set_keys_message(&set, removed_participants, key_pair), signature)
|
|
||||||
{
|
|
||||||
Err(InvalidTransaction::BadProof)?;
|
Err(InvalidTransaction::BadProof)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,12 +99,8 @@ pub fn musig_key(set: ValidatorSet, set_keys: &[Public]) -> Public {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The message for the set_keys signature.
|
/// The message for the set_keys signature.
|
||||||
pub fn set_keys_message(
|
pub fn set_keys_message(set: &ValidatorSet, key_pair: &KeyPair) -> Vec<u8> {
|
||||||
set: &ValidatorSet,
|
(b"ValidatorSets-set_keys", set, key_pair).encode()
|
||||||
removed_participants: &[Public],
|
|
||||||
key_pair: &KeyPair,
|
|
||||||
) -> Vec<u8> {
|
|
||||||
(b"ValidatorSets-set_keys", set, removed_participants, key_pair).encode()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_slashes_message(set: &ValidatorSet, slashes: &[(Public, u32)]) -> Vec<u8> {
|
pub fn report_slashes_message(set: &ValidatorSet, slashes: &[(Public, u32)]) -> Vec<u8> {
|
||||||
|
|||||||
Reference in New Issue
Block a user