mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 20:29:23 +00:00
Restore report_slashes
This does not yet handle the `SlashReport`. It solely handles the routing for it.
This commit is contained in:
@@ -109,6 +109,14 @@ pub enum Event {
|
|||||||
/// The set which accepted responsibility from the prior set.
|
/// The set which accepted responsibility from the prior set.
|
||||||
set: ValidatorSet,
|
set: ValidatorSet,
|
||||||
},
|
},
|
||||||
|
/// A slash report has been entered for this validator set.
|
||||||
|
///
|
||||||
|
/// This may be due to a slash report being published or a default being used due to one not
|
||||||
|
/// being received within time.
|
||||||
|
SlashReport {
|
||||||
|
/// The set whose slash report has been entered.
|
||||||
|
set: ExternalValidatorSet,
|
||||||
|
},
|
||||||
/// A validator set their keys on an embedded elliptic curve for a network.
|
/// A validator set their keys on an embedded elliptic curve for a network.
|
||||||
SetEmbeddedEllipticCurveKeys {
|
SetEmbeddedEllipticCurveKeys {
|
||||||
/// The validator which set their keys.
|
/// The validator which set their keys.
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ pub(crate) trait Keys {
|
|||||||
|
|
||||||
/// Clear a historic set of keys.
|
/// Clear a historic set of keys.
|
||||||
fn clear_keys(set: ExternalValidatorSet);
|
fn clear_keys(set: ExternalValidatorSet);
|
||||||
|
|
||||||
|
/// The oraclization key for a validator set.
|
||||||
|
fn oraclization_key(set: ExternalValidatorSet) -> Option<Public>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: KeysStorage> Keys for S {
|
impl<S: KeysStorage> Keys for S {
|
||||||
@@ -46,4 +49,8 @@ impl<S: KeysStorage> Keys for S {
|
|||||||
S::OraclizationKeys::remove(set);
|
S::OraclizationKeys::remove(set);
|
||||||
S::ExternalKeys::remove(set);
|
S::ExternalKeys::remove(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn oraclization_key(set: ExternalValidatorSet) -> Option<Public> {
|
||||||
|
S::OraclizationKeys::get(set)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use keys::{KeysStorage, Keys as _};
|
|||||||
#[frame_support::pallet]
|
#[frame_support::pallet]
|
||||||
mod pallet {
|
mod pallet {
|
||||||
use sp_core::sr25519::Public;
|
use sp_core::sr25519::Public;
|
||||||
|
use sp_application_crypto::RuntimePublic;
|
||||||
|
|
||||||
use frame_system::pallet_prelude::*;
|
use frame_system::pallet_prelude::*;
|
||||||
use frame_support::{pallet_prelude::*, traits::OneSessionHandler};
|
use frame_support::{pallet_prelude::*, traits::OneSessionHandler};
|
||||||
@@ -35,7 +36,9 @@ mod pallet {
|
|||||||
network_id::*,
|
network_id::*,
|
||||||
coin::*,
|
coin::*,
|
||||||
balance::*,
|
balance::*,
|
||||||
validator_sets::{Session, ExternalValidatorSet, ValidatorSet, KeyShares as KeySharesStruct},
|
validator_sets::{
|
||||||
|
Session, ExternalValidatorSet, ValidatorSet, KeyShares as KeySharesStruct, SlashReport,
|
||||||
|
},
|
||||||
address::SeraiAddress,
|
address::SeraiAddress,
|
||||||
},
|
},
|
||||||
economic_security::EconomicSecurity,
|
economic_security::EconomicSecurity,
|
||||||
@@ -126,6 +129,8 @@ mod pallet {
|
|||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
type DelayedDeallocations<T: Config> =
|
type DelayedDeallocations<T: Config> =
|
||||||
StorageDoubleMap<_, Blake2_128Concat, Public, Identity, Session, Amount, OptionQuery>;
|
StorageDoubleMap<_, Blake2_128Concat, Public, Identity, Session, Amount, OptionQuery>;
|
||||||
|
#[pallet::storage]
|
||||||
|
type PendingSlashReport<T: Config> = StorageMap<_, Identity, ExternalNetworkId, (), OptionQuery>;
|
||||||
|
|
||||||
impl<T: Config> SessionsStorage for Abstractions<T> {
|
impl<T: Config> SessionsStorage for Abstractions<T> {
|
||||||
type Config = T;
|
type Config = T;
|
||||||
@@ -138,6 +143,7 @@ mod pallet {
|
|||||||
type SelectedValidators = SelectedValidators<T>;
|
type SelectedValidators = SelectedValidators<T>;
|
||||||
type TotalAllocatedStake = TotalAllocatedStake<T>;
|
type TotalAllocatedStake = TotalAllocatedStake<T>;
|
||||||
type DelayedDeallocations = DelayedDeallocations<T>;
|
type DelayedDeallocations = DelayedDeallocations<T>;
|
||||||
|
type PendingSlashReport = PendingSlashReport<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Satisfy the `Keys` abstractions
|
// Satisfy the `Keys` abstractions
|
||||||
@@ -153,13 +159,6 @@ mod pallet {
|
|||||||
type ExternalKeys = ExternalKeys<T>;
|
type ExternalKeys = ExternalKeys<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO
|
|
||||||
/// The key for validator sets which can (and still need to) publish their slash reports.
|
|
||||||
#[pallet::storage]
|
|
||||||
pub type PendingSlashReport<T: Config> =
|
|
||||||
StorageMap<_, Identity, ExternalNetworkId, Public, OptionQuery>;
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[pallet::error]
|
#[pallet::error]
|
||||||
pub enum Error<T> {
|
pub enum Error<T> {
|
||||||
/// The provided embedded elliptic curve keys were invalid.
|
/// The provided embedded elliptic curve keys were invalid.
|
||||||
@@ -324,34 +323,6 @@ mod pallet {
|
|||||||
Sessions::<T>::decrease_allocation(network, account, amount)
|
Sessions::<T>::decrease_allocation(network, account, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This is called retire_set, yet just starts retiring the set
|
|
||||||
// Update the nomenclature within this function
|
|
||||||
pub fn retire_set(set: ValidatorSet) {
|
|
||||||
// Serai doesn't set keys and network slashes are handled by BABE/GRANDPA
|
|
||||||
if let NetworkId::External(n) = set.network {
|
|
||||||
// If the prior prior set didn't report, emit they're retired now
|
|
||||||
if PendingSlashReport::<T>::get(n).is_some() {
|
|
||||||
Self::deposit_event(Event::SetRetired {
|
|
||||||
set: ValidatorSet { network: set.network, session: Session(set.session.0 - 1) },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// This overwrites the prior value as the prior to-report set's stake presumably just
|
|
||||||
// unlocked, making their report unenforceable
|
|
||||||
let keys =
|
|
||||||
Keys::<T>::take(ExternalValidatorSet { network: n, session: set.session }).unwrap();
|
|
||||||
PendingSlashReport::<T>::set(n, Some(keys.0));
|
|
||||||
} else {
|
|
||||||
// emit the event for serai network
|
|
||||||
Self::deposit_event(Event::SetRetired { set });
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're retiring this set because the set after it accepted the handover
|
|
||||||
Self::deposit_event(Event::AcceptedHandover {
|
|
||||||
set: ValidatorSet { network: set.network, session: Session(set.session.0 + 1) },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the required stake in terms SRI for a given `Balance`.
|
/// Returns the required stake in terms SRI for a given `Balance`.
|
||||||
pub fn required_stake(balance: &ExternalBalance) -> SubstrateAmount {
|
pub fn required_stake(balance: &ExternalBalance) -> SubstrateAmount {
|
||||||
use dex_pallet::HigherPrecisionBalance;
|
use dex_pallet::HigherPrecisionBalance;
|
||||||
@@ -520,7 +491,6 @@ mod pallet {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO
|
|
||||||
#[pallet::call_index(1)]
|
#[pallet::call_index(1)]
|
||||||
#[pallet::weight((0, DispatchClass::Operational))] // TODO
|
#[pallet::weight((0, DispatchClass::Operational))] // TODO
|
||||||
pub fn report_slashes(
|
pub fn report_slashes(
|
||||||
@@ -531,24 +501,13 @@ mod pallet {
|
|||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
ensure_none(origin)?;
|
ensure_none(origin)?;
|
||||||
|
|
||||||
// signature isn't checked as this is an unsigned transaction, and validate_unsigned
|
// `signature` is checked within `ValidateUnsigned`
|
||||||
// (called by pre_dispatch) checks it
|
|
||||||
let _ = signature;
|
let _ = signature;
|
||||||
|
|
||||||
// TODO: Handle slashes
|
Abstractions::<T>::handle_slash_report(network, slashes);
|
||||||
let _ = slashes;
|
|
||||||
|
|
||||||
// Emit set retireed
|
|
||||||
Pallet::<T>::deposit_event(Event::SetRetired {
|
|
||||||
set: ValidatorSet {
|
|
||||||
network: network.into(),
|
|
||||||
session: Session(Self::session(NetworkId::from(network)).unwrap().0 - 1),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
#[pallet::call_index(2)]
|
#[pallet::call_index(2)]
|
||||||
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
||||||
@@ -693,7 +652,6 @@ mod pallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify the signature with the MuSig key of the signers
|
// Verify the signature with the MuSig key of the signers
|
||||||
use sp_application_crypto::RuntimePublic;
|
|
||||||
if !set.musig_key(&signers).verify(&set.set_keys_message(key_pair), &signature.0.into()) {
|
if !set.musig_key(&signers).verify(&set.set_keys_message(key_pair), &signature.0.into()) {
|
||||||
Err(InvalidTransaction::BadProof)?;
|
Err(InvalidTransaction::BadProof)?;
|
||||||
}
|
}
|
||||||
@@ -704,30 +662,23 @@ mod pallet {
|
|||||||
.propagate(true)
|
.propagate(true)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
/* TODO
|
|
||||||
Call::report_slashes { network, ref slashes, ref signature } => {
|
Call::report_slashes { network, ref slashes, ref signature } => {
|
||||||
let network = *network;
|
let network = *network;
|
||||||
let Some(key) = PendingSlashReport::<T>::take(network) else {
|
|
||||||
// Assumed already published
|
let Some(key) = Abstractions::<T>::waiting_for_slash_report(network) else {
|
||||||
Err(InvalidTransaction::Stale)?
|
Err(InvalidTransaction::Stale)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// There must have been a previous session is PendingSlashReport is populated
|
if !key.verify(&slashes.report_slashes_message(), &signature.0.into()) {
|
||||||
let set = ExternalValidatorSet {
|
|
||||||
network,
|
|
||||||
session: Session(Self::session(NetworkId::from(network)).unwrap().0 - 1),
|
|
||||||
};
|
|
||||||
if !key.verify(&slashes.report_slashes_message(), signature) {
|
|
||||||
Err(InvalidTransaction::BadProof)?;
|
Err(InvalidTransaction::BadProof)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidTransaction::with_tag_prefix("ValidatorSets")
|
ValidTransaction::with_tag_prefix("ValidatorSets")
|
||||||
.and_provides((1, set))
|
.and_provides((1, key))
|
||||||
.longevity(MAX_KEY_SHARES_PER_SET_U32.into())
|
.longevity(KeySharesStruct::MAX_PER_SET_U32.into())
|
||||||
.propagate(true)
|
.propagate(true)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
Call::set_embedded_elliptic_curve_keys { .. } |
|
Call::set_embedded_elliptic_curve_keys { .. } |
|
||||||
Call::allocate { .. } |
|
Call::allocate { .. } |
|
||||||
Call::deallocate { .. } |
|
Call::deallocate { .. } |
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ use sp_core::{Encode, Decode, ConstU32, sr25519::Public, bounded::BoundedVec};
|
|||||||
|
|
||||||
use serai_abi::{
|
use serai_abi::{
|
||||||
primitives::{
|
primitives::{
|
||||||
network_id::NetworkId,
|
network_id::{ExternalNetworkId, NetworkId},
|
||||||
balance::Amount,
|
balance::Amount,
|
||||||
validator_sets::{KeyShares as KeySharesStruct, Session, ExternalValidatorSet, ValidatorSet},
|
validator_sets::{
|
||||||
|
KeyShares as KeySharesStruct, Session, ExternalValidatorSet, ValidatorSet, SlashReport,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
validator_sets::{DeallocationTimeline, Event},
|
validator_sets::{DeallocationTimeline, Event},
|
||||||
};
|
};
|
||||||
@@ -76,6 +78,11 @@ pub(crate) trait SessionsStorage: EmbeddedEllipticCurveKeys + Allocations + Keys
|
|||||||
///
|
///
|
||||||
/// This is opaque and to be exclusively read/write by `Sessions`.
|
/// This is opaque and to be exclusively read/write by `Sessions`.
|
||||||
type DelayedDeallocations: StorageDoubleMap<Public, Session, Amount, Query = Option<Amount>>;
|
type DelayedDeallocations: StorageDoubleMap<Public, Session, Amount, Query = Option<Amount>>;
|
||||||
|
|
||||||
|
/// Networks for which we're awaiting slash reports.
|
||||||
|
///
|
||||||
|
/// This is opaque and to be exclusively read/write by `Sessions`.
|
||||||
|
type PendingSlashReport: StorageMap<ExternalNetworkId, (), Query = Option<()>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The storage key for the SelectedValidators map.
|
/// The storage key for the SelectedValidators map.
|
||||||
@@ -196,6 +203,11 @@ pub(crate) trait Sessions {
|
|||||||
session: Session,
|
session: Session,
|
||||||
) -> Result<Amount, DeallocationError>;
|
) -> Result<Amount, DeallocationError>;
|
||||||
|
|
||||||
|
/// Handle a slash report.
|
||||||
|
///
|
||||||
|
/// This will panic if this slash report isn't pending.
|
||||||
|
fn handle_slash_report(network: ExternalNetworkId, slashes: SlashReport);
|
||||||
|
|
||||||
/// The currently active session for a network.
|
/// The currently active session for a network.
|
||||||
fn current_session(network: NetworkId) -> Option<Session>;
|
fn current_session(network: NetworkId) -> Option<Session>;
|
||||||
|
|
||||||
@@ -235,6 +247,11 @@ pub(crate) trait Sessions {
|
|||||||
.map(|(validator, _key_shares)| (validator, validator))
|
.map(|(validator, _key_shares)| (validator, validator))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this network is awaiting a slash report.
|
||||||
|
///
|
||||||
|
/// If so, this returns the key which should publish the slash report.
|
||||||
|
fn waiting_for_slash_report(network: ExternalNetworkId) -> Option<Public>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Storage: SessionsStorage> Sessions for Storage {
|
impl<Storage: SessionsStorage> Sessions for Storage {
|
||||||
@@ -322,7 +339,7 @@ impl<Storage: SessionsStorage> Sessions for Storage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn accept_handover(network: NetworkId) {
|
fn accept_handover(network: NetworkId) {
|
||||||
let current = {
|
let (prior, current) = {
|
||||||
let current = Storage::CurrentSession::get(network);
|
let current = Storage::CurrentSession::get(network);
|
||||||
let latest_decided = Storage::LatestDecidedSession::get(network)
|
let latest_decided = Storage::LatestDecidedSession::get(network)
|
||||||
.expect("accepting handover but never decided a session");
|
.expect("accepting handover but never decided a session");
|
||||||
@@ -333,8 +350,8 @@ impl<Storage: SessionsStorage> Sessions for Storage {
|
|||||||
);
|
);
|
||||||
// Set the CurrentSession variable
|
// Set the CurrentSession variable
|
||||||
Storage::CurrentSession::set(network, Some(latest_decided));
|
Storage::CurrentSession::set(network, Some(latest_decided));
|
||||||
// Return `latest_decided` as `current` as it is now current
|
// Return `latest_decided` as `current` as it is now current, and `current` as `prior`
|
||||||
latest_decided
|
(current, latest_decided)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut total_allocated_stake = Amount(0);
|
let mut total_allocated_stake = Amount(0);
|
||||||
@@ -348,6 +365,23 @@ impl<Storage: SessionsStorage> Sessions for Storage {
|
|||||||
// Update the total allocated stake variable to the current session
|
// Update the total allocated stake variable to the current session
|
||||||
Storage::TotalAllocatedStake::set(network, Some(total_allocated_stake));
|
Storage::TotalAllocatedStake::set(network, Some(total_allocated_stake));
|
||||||
|
|
||||||
|
match network {
|
||||||
|
NetworkId::Serai => {}
|
||||||
|
NetworkId::External(network) => {
|
||||||
|
// If this network never submitted its slash report, treat it as submitting `vec![]`
|
||||||
|
if Storage::PendingSlashReport::take(network).is_some() {
|
||||||
|
Core::<Storage::Config>::emit_event(Event::SlashReport {
|
||||||
|
set: ExternalValidatorSet {
|
||||||
|
network,
|
||||||
|
session: prior.expect("pending slash report yet no prior session"),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Mark this network as pending a slash report
|
||||||
|
Storage::PendingSlashReport::insert(network, ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clean-up the historic set's storage, if one exists
|
// Clean-up the historic set's storage, if one exists
|
||||||
if let Some(historic_session) = current.0.checked_sub(2).map(Session) {
|
if let Some(historic_session) = current.0.checked_sub(2).map(Session) {
|
||||||
let historic_set = ValidatorSet { network, session: historic_session };
|
let historic_set = ValidatorSet { network, session: historic_session };
|
||||||
@@ -537,6 +571,22 @@ impl<Storage: SessionsStorage> Sessions for Storage {
|
|||||||
Ok(DeallocationTimeline::Immediate)
|
Ok(DeallocationTimeline::Immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_slash_report(network: ExternalNetworkId, _slashes: SlashReport) {
|
||||||
|
Storage::PendingSlashReport::take(network)
|
||||||
|
.expect("handling a slash report which wasn't pending");
|
||||||
|
|
||||||
|
let current_session =
|
||||||
|
Self::current_session(network.into()).expect("handling slash report yet no current session");
|
||||||
|
let prior_session = Session(
|
||||||
|
current_session.0.checked_sub(1).expect("handling slash report yet no prior session"),
|
||||||
|
);
|
||||||
|
Core::<Storage::Config>::emit_event(Event::SlashReport {
|
||||||
|
set: ExternalValidatorSet { network, session: prior_session },
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Actually handle `_slashes`
|
||||||
|
}
|
||||||
|
|
||||||
fn claim_delayed_deallocation(
|
fn claim_delayed_deallocation(
|
||||||
validator: Public,
|
validator: Public,
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
@@ -581,4 +631,19 @@ impl<Storage: SessionsStorage> Sessions for Storage {
|
|||||||
fn selected_validators(set: ValidatorSet) -> impl Iterator<Item = (Public, KeySharesStruct)> {
|
fn selected_validators(set: ValidatorSet) -> impl Iterator<Item = (Public, KeySharesStruct)> {
|
||||||
selected_validators::<Storage::SelectedValidators>(set)
|
selected_validators::<Storage::SelectedValidators>(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn waiting_for_slash_report(network: ExternalNetworkId) -> Option<Public> {
|
||||||
|
if !Storage::PendingSlashReport::contains_key(network) {
|
||||||
|
None?;
|
||||||
|
}
|
||||||
|
let current_session = Self::current_session(network.into())
|
||||||
|
.expect("network awaiting slash report yet no current session");
|
||||||
|
let prior_session = Session(
|
||||||
|
current_session.0.checked_sub(1).expect("network awaiting slash report yet no prior session"),
|
||||||
|
);
|
||||||
|
Some(
|
||||||
|
Storage::oraclization_key(ExternalValidatorSet { network, session: prior_session })
|
||||||
|
.expect("no oraclization key for set waiting for a slash report"),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user