mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-16 21:21:03 +00:00
[frame/im-online] remove network state from heartbeats (#14251)
* [frame/im-online] remove `external_addresses` from heartbeats Users should use DHT for discovering new nodes. The reason for adding external addresses was unstable work of authority discovery (see https://github.com/paritytech/substrate/issues/2719), which is now stable. Hence we can safely remove `external_addresses`. Refs https://github.com/paritytech/polkadot/issues/7181 * remove unused import * run benchmark * remove external_addresses from offchain NetworkState * add missing fn to TestNetwork * Revert "run benchmark" This reverts commit a282042c2d6bf8bae2c383f6e2699c3fe2970a3d. * update weights * address @bkchr comments * remove duplicate fn * cleanup benchmarking.rs * fix executor tests * remove peer_id from hearbeat as well https://github.com/paritytech/substrate/pull/14251#discussion_r1210887220 * remove MaxPeerDataEncodingSize * change storage value type to `()` https://github.com/paritytech/substrate/pull/14251#discussion_r1214268931 * scaffold storage migration * no need to check the type actually * remove unnecessary types from v0 mod * add a test for migration * expose Config types + pre_upgrade and post_upgrade working fn * fix test * replace dummy type with ConstU32 * add some comments to migration test * fix comment * respond to @bkchr comments * use BoundedOpaqueNetworkState::default intead of using default for each field
This commit is contained in:
@@ -26,8 +26,7 @@
|
||||
//! in the current era or session.
|
||||
//!
|
||||
//! The heartbeat is a signed transaction, which was signed using the session key
|
||||
//! and includes the recent best block number of the local validators chain as well
|
||||
//! as the [NetworkState](../../client/offchain/struct.NetworkState.html).
|
||||
//! and includes the recent best block number of the local validators chain.
|
||||
//! It is submitted as an Unsigned Transaction via off-chain workers.
|
||||
//!
|
||||
//! - [`Config`]
|
||||
@@ -78,23 +77,27 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
mod benchmarking;
|
||||
pub mod migration;
|
||||
mod mock;
|
||||
mod tests;
|
||||
pub mod weights;
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use frame_support::{
|
||||
pallet_prelude::*,
|
||||
traits::{
|
||||
EstimateNextSessionRotation, Get, OneSessionHandler, ValidatorSet,
|
||||
ValidatorSetWithIdentification, WrapperOpaque,
|
||||
ValidatorSetWithIdentification,
|
||||
},
|
||||
BoundedSlice, WeakBoundedVec,
|
||||
};
|
||||
use frame_system::offchain::{SendTransactionTypes, SubmitTransaction};
|
||||
use frame_system::{
|
||||
offchain::{SendTransactionTypes, SubmitTransaction},
|
||||
pallet_prelude::*,
|
||||
};
|
||||
pub use pallet::*;
|
||||
use scale_info::TypeInfo;
|
||||
use sp_application_crypto::RuntimeAppPublic;
|
||||
use sp_core::offchain::OpaqueNetworkState;
|
||||
use sp_runtime::{
|
||||
offchain::storage::{MutateStorageError, StorageRetrievalError, StorageValueRef},
|
||||
traits::{AtLeast32BitUnsigned, Convert, Saturating, TrailingZeroInput},
|
||||
@@ -190,7 +193,6 @@ enum OffchainErr<BlockNumber> {
|
||||
AlreadyOnline(u32),
|
||||
FailedSigning,
|
||||
FailedToAcquireLock,
|
||||
NetworkState,
|
||||
SubmitTransaction,
|
||||
}
|
||||
|
||||
@@ -206,7 +208,6 @@ impl<BlockNumber: sp_std::fmt::Debug> sp_std::fmt::Debug for OffchainErr<BlockNu
|
||||
},
|
||||
OffchainErr::FailedSigning => write!(fmt, "Failed to sign heartbeat"),
|
||||
OffchainErr::FailedToAcquireLock => write!(fmt, "Failed to acquire lock"),
|
||||
OffchainErr::NetworkState => write!(fmt, "Failed to fetch network state"),
|
||||
OffchainErr::SubmitTransaction => write!(fmt, "Failed to submit transaction"),
|
||||
}
|
||||
}
|
||||
@@ -222,8 +223,6 @@ where
|
||||
{
|
||||
/// Block number at the time heartbeat is created..
|
||||
pub block_number: BlockNumber,
|
||||
/// A state of local network (peer id and external addresses)
|
||||
pub network_state: OpaqueNetworkState,
|
||||
/// Index of the current session.
|
||||
pub session_index: SessionIndex,
|
||||
/// An index of the authority on the list of validators.
|
||||
@@ -232,64 +231,6 @@ where
|
||||
pub validators_len: u32,
|
||||
}
|
||||
|
||||
/// A type that is the same as [`OpaqueNetworkState`] but with [`Vec`] replaced with
|
||||
/// [`WeakBoundedVec<Limit>`] where Limit is the respective size limit
|
||||
/// `PeerIdEncodingLimit` represents the size limit of the encoding of `PeerId`
|
||||
/// `MultiAddrEncodingLimit` represents the size limit of the encoding of `MultiAddr`
|
||||
/// `AddressesLimit` represents the size limit of the vector of peers connected
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[codec(mel_bound())]
|
||||
#[scale_info(skip_type_params(PeerIdEncodingLimit, MultiAddrEncodingLimit, AddressesLimit))]
|
||||
pub struct BoundedOpaqueNetworkState<PeerIdEncodingLimit, MultiAddrEncodingLimit, AddressesLimit>
|
||||
where
|
||||
PeerIdEncodingLimit: Get<u32>,
|
||||
MultiAddrEncodingLimit: Get<u32>,
|
||||
AddressesLimit: Get<u32>,
|
||||
{
|
||||
/// PeerId of the local node in SCALE encoded.
|
||||
pub peer_id: WeakBoundedVec<u8, PeerIdEncodingLimit>,
|
||||
/// List of addresses the node knows it can be reached as.
|
||||
pub external_addresses:
|
||||
WeakBoundedVec<WeakBoundedVec<u8, MultiAddrEncodingLimit>, AddressesLimit>,
|
||||
}
|
||||
|
||||
impl<PeerIdEncodingLimit: Get<u32>, MultiAddrEncodingLimit: Get<u32>, AddressesLimit: Get<u32>>
|
||||
BoundedOpaqueNetworkState<PeerIdEncodingLimit, MultiAddrEncodingLimit, AddressesLimit>
|
||||
{
|
||||
fn force_from(ons: &OpaqueNetworkState) -> Self {
|
||||
let peer_id = WeakBoundedVec::<_, PeerIdEncodingLimit>::force_from(
|
||||
ons.peer_id.0.clone(),
|
||||
Some(
|
||||
"Warning: The size of the encoding of PeerId \
|
||||
is bigger than expected. A runtime configuration \
|
||||
adjustment may be needed.",
|
||||
),
|
||||
);
|
||||
|
||||
let external_addresses = WeakBoundedVec::<_, AddressesLimit>::force_from(
|
||||
ons.external_addresses
|
||||
.iter()
|
||||
.map(|x| {
|
||||
WeakBoundedVec::<_, MultiAddrEncodingLimit>::force_from(
|
||||
x.0.clone(),
|
||||
Some(
|
||||
"Warning: The size of the encoding of MultiAddr \
|
||||
is bigger than expected. A runtime configuration \
|
||||
adjustment may be needed.",
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
Some(
|
||||
"Warning: The network has more peers than expected \
|
||||
A runtime configuration adjustment may be needed.",
|
||||
),
|
||||
);
|
||||
|
||||
Self { peer_id, external_addresses }
|
||||
}
|
||||
}
|
||||
|
||||
/// A type for representing the validator id in a session.
|
||||
pub type ValidatorId<T> = <<T as Config>::ValidatorSet as ValidatorSet<
|
||||
<T as frame_system::Config>::AccountId,
|
||||
@@ -309,10 +250,12 @@ type OffchainResult<T, A> = Result<A, OffchainErr<<T as frame_system::Config>::B
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
/// The current storage version.
|
||||
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::storage_version(STORAGE_VERSION)]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[pallet::config]
|
||||
@@ -331,10 +274,6 @@ pub mod pallet {
|
||||
/// The maximum number of peers to be stored in `ReceivedHeartbeats`
|
||||
type MaxPeerInHeartbeats: Get<u32>;
|
||||
|
||||
/// The maximum size of the encoding of `PeerId` and `MultiAddr` that are coming
|
||||
/// from the hearbeat
|
||||
type MaxPeerDataEncodingSize: Get<u32>;
|
||||
|
||||
/// The overarching event type.
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
|
||||
@@ -400,38 +339,25 @@ pub mod pallet {
|
||||
/// more accurate then the value we calculate for `HeartbeatAfter`.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn heartbeat_after)]
|
||||
pub(crate) type HeartbeatAfter<T: Config> = StorageValue<_, T::BlockNumber, ValueQuery>;
|
||||
pub(super) type HeartbeatAfter<T: Config> = StorageValue<_, T::BlockNumber, ValueQuery>;
|
||||
|
||||
/// The current set of keys that may issue a heartbeat.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn keys)]
|
||||
pub(crate) type Keys<T: Config> =
|
||||
pub(super) type Keys<T: Config> =
|
||||
StorageValue<_, WeakBoundedVec<T::AuthorityId, T::MaxKeys>, ValueQuery>;
|
||||
|
||||
/// For each session index, we keep a mapping of `SessionIndex` and `AuthIndex` to
|
||||
/// `WrapperOpaque<BoundedOpaqueNetworkState>`.
|
||||
/// For each session index, we keep a mapping of `SessionIndex` and `AuthIndex`.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn received_heartbeats)]
|
||||
pub(crate) type ReceivedHeartbeats<T: Config> = StorageDoubleMap<
|
||||
_,
|
||||
Twox64Concat,
|
||||
SessionIndex,
|
||||
Twox64Concat,
|
||||
AuthIndex,
|
||||
WrapperOpaque<
|
||||
BoundedOpaqueNetworkState<
|
||||
T::MaxPeerDataEncodingSize,
|
||||
T::MaxPeerDataEncodingSize,
|
||||
T::MaxPeerInHeartbeats,
|
||||
>,
|
||||
>,
|
||||
>;
|
||||
pub(super) type ReceivedHeartbeats<T: Config> =
|
||||
StorageDoubleMap<_, Twox64Concat, SessionIndex, Twox64Concat, AuthIndex, bool>;
|
||||
|
||||
/// For each session index, we keep a mapping of `ValidatorId<T>` to the
|
||||
/// number of blocks authored by the given authority.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn authored_blocks)]
|
||||
pub(crate) type AuthoredBlocks<T: Config> = StorageDoubleMap<
|
||||
pub(super) type AuthoredBlocks<T: Config> = StorageDoubleMap<
|
||||
_,
|
||||
Twox64Concat,
|
||||
SessionIndex,
|
||||
@@ -457,16 +383,13 @@ pub mod pallet {
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// ## Complexity:
|
||||
/// - `O(K + E)` where K is length of `Keys` (heartbeat.validators_len) and E is length of
|
||||
/// `heartbeat.network_state.external_address`
|
||||
/// - `O(K)` where K is length of `Keys` (heartbeat.validators_len)
|
||||
/// - `O(K)`: decoding of length `K`
|
||||
/// - `O(E)`: decoding/encoding of length `E`
|
||||
// NOTE: the weight includes the cost of validate_unsigned as it is part of the cost to
|
||||
// import block with such an extrinsic.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(<T as Config>::WeightInfo::validate_unsigned_and_then_heartbeat(
|
||||
heartbeat.validators_len as u32,
|
||||
heartbeat.network_state.external_addresses.len() as u32,
|
||||
heartbeat.validators_len,
|
||||
))]
|
||||
pub fn heartbeat(
|
||||
origin: OriginFor<T>,
|
||||
@@ -479,22 +402,13 @@ pub mod pallet {
|
||||
|
||||
let current_session = T::ValidatorSet::session_index();
|
||||
let exists =
|
||||
ReceivedHeartbeats::<T>::contains_key(¤t_session, &heartbeat.authority_index);
|
||||
ReceivedHeartbeats::<T>::contains_key(current_session, heartbeat.authority_index);
|
||||
let keys = Keys::<T>::get();
|
||||
let public = keys.get(heartbeat.authority_index as usize);
|
||||
if let (false, Some(public)) = (exists, public) {
|
||||
Self::deposit_event(Event::<T>::HeartbeatReceived { authority_id: public.clone() });
|
||||
|
||||
let network_state_bounded = BoundedOpaqueNetworkState::<
|
||||
T::MaxPeerDataEncodingSize,
|
||||
T::MaxPeerDataEncodingSize,
|
||||
T::MaxPeerInHeartbeats,
|
||||
>::force_from(&heartbeat.network_state);
|
||||
ReceivedHeartbeats::<T>::insert(
|
||||
¤t_session,
|
||||
&heartbeat.authority_index,
|
||||
WrapperOpaque::from(network_state_bounded),
|
||||
);
|
||||
ReceivedHeartbeats::<T>::insert(current_session, heartbeat.authority_index, true);
|
||||
|
||||
Ok(())
|
||||
} else if exists {
|
||||
@@ -618,22 +532,22 @@ impl<T: Config> Pallet<T> {
|
||||
fn is_online_aux(authority_index: AuthIndex, authority: &ValidatorId<T>) -> bool {
|
||||
let current_session = T::ValidatorSet::session_index();
|
||||
|
||||
ReceivedHeartbeats::<T>::contains_key(¤t_session, &authority_index) ||
|
||||
AuthoredBlocks::<T>::get(¤t_session, authority) != 0
|
||||
ReceivedHeartbeats::<T>::contains_key(current_session, authority_index) ||
|
||||
AuthoredBlocks::<T>::get(current_session, authority) != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if a heartbeat has been received for the authority at `authority_index` in
|
||||
/// the authorities series, during the current session. Otherwise `false`.
|
||||
pub fn received_heartbeat_in_current_session(authority_index: AuthIndex) -> bool {
|
||||
let current_session = T::ValidatorSet::session_index();
|
||||
ReceivedHeartbeats::<T>::contains_key(¤t_session, &authority_index)
|
||||
ReceivedHeartbeats::<T>::contains_key(current_session, authority_index)
|
||||
}
|
||||
|
||||
/// Note that the given authority has authored a block in the current session.
|
||||
fn note_authorship(author: ValidatorId<T>) {
|
||||
let current_session = T::ValidatorSet::session_index();
|
||||
|
||||
AuthoredBlocks::<T>::mutate(¤t_session, author, |authored| *authored += 1);
|
||||
AuthoredBlocks::<T>::mutate(current_session, author, |authored| *authored += 1);
|
||||
}
|
||||
|
||||
pub(crate) fn send_heartbeats(
|
||||
@@ -705,15 +619,8 @@ impl<T: Config> Pallet<T> {
|
||||
) -> OffchainResult<T, ()> {
|
||||
// A helper function to prepare heartbeat call.
|
||||
let prepare_heartbeat = || -> OffchainResult<T, Call<T>> {
|
||||
let network_state =
|
||||
sp_io::offchain::network_state().map_err(|_| OffchainErr::NetworkState)?;
|
||||
let heartbeat = Heartbeat {
|
||||
block_number,
|
||||
network_state,
|
||||
session_index,
|
||||
authority_index,
|
||||
validators_len,
|
||||
};
|
||||
let heartbeat =
|
||||
Heartbeat { block_number, session_index, authority_index, validators_len };
|
||||
|
||||
let signature = key.sign(&heartbeat.encode()).ok_or(OffchainErr::FailedSigning)?;
|
||||
|
||||
@@ -887,9 +794,9 @@ impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
|
||||
// current session, they have already been processed and won't be needed
|
||||
// anymore.
|
||||
#[allow(deprecated)]
|
||||
ReceivedHeartbeats::<T>::remove_prefix(&T::ValidatorSet::session_index(), None);
|
||||
ReceivedHeartbeats::<T>::remove_prefix(T::ValidatorSet::session_index(), None);
|
||||
#[allow(deprecated)]
|
||||
AuthoredBlocks::<T>::remove_prefix(&T::ValidatorSet::session_index(), None);
|
||||
AuthoredBlocks::<T>::remove_prefix(T::ValidatorSet::session_index(), None);
|
||||
|
||||
if offenders.is_empty() {
|
||||
Self::deposit_event(Event::<T>::AllGood);
|
||||
|
||||
Reference in New Issue
Block a user