mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 02:57:57 +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:
@@ -37,7 +37,6 @@ fn should_submit_unsigned_transaction() {
|
||||
pallet_im_online::sr25519::AuthoritySignature::try_from(vec![0; 64]).unwrap();
|
||||
let heartbeat_data = pallet_im_online::Heartbeat {
|
||||
block_number: 1,
|
||||
network_state: Default::default(),
|
||||
session_index: 1,
|
||||
authority_index: 0,
|
||||
validators_len: 0,
|
||||
|
||||
@@ -328,8 +328,9 @@ impl InstanceFilter<RuntimeCall> for ProxyType {
|
||||
RuntimeCall::Elections(..) |
|
||||
RuntimeCall::Treasury(..)
|
||||
),
|
||||
ProxyType::Staking =>
|
||||
matches!(c, RuntimeCall::Staking(..) | RuntimeCall::FastUnstake(..)),
|
||||
ProxyType::Staking => {
|
||||
matches!(c, RuntimeCall::Staking(..) | RuntimeCall::FastUnstake(..))
|
||||
},
|
||||
}
|
||||
}
|
||||
fn is_superset(&self, o: &Self) -> bool {
|
||||
@@ -1261,7 +1262,6 @@ parameter_types! {
|
||||
pub const MaxAuthorities: u32 = 100;
|
||||
pub const MaxKeys: u32 = 10_000;
|
||||
pub const MaxPeerInHeartbeats: u32 = 10_000;
|
||||
pub const MaxPeerDataEncodingSize: u32 = 1_000;
|
||||
}
|
||||
|
||||
impl<LocalCall> frame_system::offchain::CreateSignedTransaction<LocalCall> for Runtime
|
||||
@@ -1329,7 +1329,6 @@ impl pallet_im_online::Config for Runtime {
|
||||
type WeightInfo = pallet_im_online::weights::SubstrateWeight<Runtime>;
|
||||
type MaxKeys = MaxKeys;
|
||||
type MaxPeerInHeartbeats = MaxPeerInHeartbeats;
|
||||
type MaxPeerDataEncodingSize = MaxPeerDataEncodingSize;
|
||||
}
|
||||
|
||||
impl pallet_offences::Config for Runtime {
|
||||
|
||||
@@ -324,7 +324,6 @@ impl AsyncApi {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use libp2p::PeerId;
|
||||
use sc_client_db::offchain::LocalStorage;
|
||||
use sc_network::{
|
||||
config::MultiaddrWithPeerId, types::ProtocolName, NetworkPeers, NetworkStateInfo,
|
||||
|
||||
@@ -24,7 +24,6 @@ use super::*;
|
||||
use frame_benchmarking::v1::benchmarks;
|
||||
use frame_support::{traits::UnfilteredDispatchable, WeakBoundedVec};
|
||||
use frame_system::RawOrigin;
|
||||
use sp_core::{offchain::OpaqueMultiaddr, OpaquePeerId};
|
||||
use sp_runtime::{
|
||||
traits::{ValidateUnsigned, Zero},
|
||||
transaction_validity::TransactionSource,
|
||||
@@ -33,11 +32,9 @@ use sp_runtime::{
|
||||
use crate::Pallet as ImOnline;
|
||||
|
||||
const MAX_KEYS: u32 = 1000;
|
||||
const MAX_EXTERNAL_ADDRESSES: u32 = 100;
|
||||
|
||||
pub fn create_heartbeat<T: Config>(
|
||||
k: u32,
|
||||
e: u32,
|
||||
) -> Result<
|
||||
(crate::Heartbeat<T::BlockNumber>, <T::AuthorityId as RuntimeAppPublic>::Signature),
|
||||
&'static str,
|
||||
@@ -50,13 +47,8 @@ pub fn create_heartbeat<T: Config>(
|
||||
.map_err(|()| "More than the maximum number of keys provided")?;
|
||||
Keys::<T>::put(bounded_keys);
|
||||
|
||||
let network_state = OpaqueNetworkState {
|
||||
peer_id: OpaquePeerId::default(),
|
||||
external_addresses: vec![OpaqueMultiaddr::new(vec![0; 32]); e as usize],
|
||||
};
|
||||
let input_heartbeat = Heartbeat {
|
||||
block_number: T::BlockNumber::zero(),
|
||||
network_state,
|
||||
session_index: 0,
|
||||
authority_index: k - 1,
|
||||
validators_len: keys.len() as u32,
|
||||
@@ -73,15 +65,13 @@ benchmarks! {
|
||||
#[extra]
|
||||
heartbeat {
|
||||
let k in 1 .. MAX_KEYS;
|
||||
let e in 1 .. MAX_EXTERNAL_ADDRESSES;
|
||||
let (input_heartbeat, signature) = create_heartbeat::<T>(k, e)?;
|
||||
let (input_heartbeat, signature) = create_heartbeat::<T>(k)?;
|
||||
}: _(RawOrigin::None, input_heartbeat, signature)
|
||||
|
||||
#[extra]
|
||||
validate_unsigned {
|
||||
let k in 1 .. MAX_KEYS;
|
||||
let e in 1 .. MAX_EXTERNAL_ADDRESSES;
|
||||
let (input_heartbeat, signature) = create_heartbeat::<T>(k, e)?;
|
||||
let (input_heartbeat, signature) = create_heartbeat::<T>(k)?;
|
||||
let call = Call::heartbeat { heartbeat: input_heartbeat, signature };
|
||||
}: {
|
||||
ImOnline::<T>::validate_unsigned(TransactionSource::InBlock, &call)
|
||||
@@ -90,8 +80,7 @@ benchmarks! {
|
||||
|
||||
validate_unsigned_and_then_heartbeat {
|
||||
let k in 1 .. MAX_KEYS;
|
||||
let e in 1 .. MAX_EXTERNAL_ADDRESSES;
|
||||
let (input_heartbeat, signature) = create_heartbeat::<T>(k, e)?;
|
||||
let (input_heartbeat, signature) = create_heartbeat::<T>(k)?;
|
||||
let call = Call::heartbeat { heartbeat: input_heartbeat, signature };
|
||||
let call_enc = call.encode();
|
||||
}: {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Storage migrations for the im-online pallet.
|
||||
|
||||
use super::*;
|
||||
use frame_support::{storage_alias, traits::OnRuntimeUpgrade};
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
use frame_support::ensure;
|
||||
#[cfg(feature = "try-runtime")]
|
||||
use sp_runtime::TryRuntimeError;
|
||||
|
||||
/// The log target.
|
||||
const TARGET: &str = "runtime::im-online::migration::v1";
|
||||
|
||||
/// The original data layout of the im-online pallet (`ReceivedHeartbeats` storage item).
|
||||
mod v0 {
|
||||
use super::*;
|
||||
use frame_support::traits::WrapperOpaque;
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
pub(super) struct BoundedOpaqueNetworkState {
|
||||
/// PeerId of the local node in SCALE encoded.
|
||||
pub peer_id: Vec<u8>,
|
||||
/// List of addresses the node knows it can be reached as.
|
||||
pub external_addresses: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[storage_alias]
|
||||
pub(super) type ReceivedHeartbeats<T: Config> = StorageDoubleMap<
|
||||
Pallet<T>,
|
||||
Twox64Concat,
|
||||
SessionIndex,
|
||||
Twox64Concat,
|
||||
AuthIndex,
|
||||
WrapperOpaque<BoundedOpaqueNetworkState>,
|
||||
>;
|
||||
}
|
||||
|
||||
pub mod v1 {
|
||||
use super::*;
|
||||
|
||||
/// Simple migration that replaces `ReceivedHeartbeats` values with `true`.
|
||||
pub struct Migration<T>(sp_std::marker::PhantomData<T>);
|
||||
|
||||
impl<T: Config> OnRuntimeUpgrade for Migration<T> {
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
||||
ensure!(StorageVersion::get::<Pallet<T>>() == 0, "can only upgrade from version 0");
|
||||
|
||||
let count = v0::ReceivedHeartbeats::<T>::iter().count();
|
||||
log::info!(target: TARGET, "Migrating {} received heartbeats", count);
|
||||
|
||||
Ok((count as u32).encode())
|
||||
}
|
||||
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
let mut weight = T::DbWeight::get().reads(1);
|
||||
if StorageVersion::get::<Pallet<T>>() != 0 {
|
||||
log::warn!(
|
||||
target: TARGET,
|
||||
"Skipping migration because current storage version is not 0"
|
||||
);
|
||||
return weight
|
||||
}
|
||||
|
||||
let heartbeats = v0::ReceivedHeartbeats::<T>::drain().collect::<Vec<_>>();
|
||||
|
||||
weight.saturating_accrue(T::DbWeight::get().reads(heartbeats.len() as u64));
|
||||
weight.saturating_accrue(T::DbWeight::get().writes(heartbeats.len() as u64));
|
||||
|
||||
for (session_index, auth_index, _) in heartbeats {
|
||||
log::trace!(
|
||||
target: TARGET,
|
||||
"Migrated received heartbeat for {:?}...",
|
||||
(session_index, auth_index)
|
||||
);
|
||||
crate::ReceivedHeartbeats::<T>::insert(session_index, auth_index, true);
|
||||
}
|
||||
|
||||
StorageVersion::new(1).put::<Pallet<T>>();
|
||||
weight.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn post_upgrade(state: Vec<u8>) -> DispatchResult {
|
||||
let old_received_heartbeats: u32 =
|
||||
Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
|
||||
let new_received_heartbeats = crate::ReceivedHeartbeats::<T>::iter().count();
|
||||
|
||||
if new_received_heartbeats != old_received_heartbeats as usize {
|
||||
log::error!(
|
||||
target: TARGET,
|
||||
"migrated {} received heartbeats, expected {}",
|
||||
new_received_heartbeats,
|
||||
old_received_heartbeats
|
||||
);
|
||||
}
|
||||
ensure!(StorageVersion::get::<Pallet<T>>() == 1, "must upgrade");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "try-runtime")]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::mock::{new_test_ext, Runtime as T};
|
||||
use frame_support::traits::WrapperOpaque;
|
||||
|
||||
#[test]
|
||||
fn migration_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(StorageVersion::get::<Pallet<T>>(), 0);
|
||||
|
||||
// Insert some received heartbeats into the v0 storage:
|
||||
let current_session = <T as pallet::Config>::ValidatorSet::session_index();
|
||||
v0::ReceivedHeartbeats::<T>::insert(
|
||||
¤t_session,
|
||||
0,
|
||||
WrapperOpaque(v0::BoundedOpaqueNetworkState::default()),
|
||||
);
|
||||
v0::ReceivedHeartbeats::<T>::insert(
|
||||
¤t_session,
|
||||
1,
|
||||
WrapperOpaque(v0::BoundedOpaqueNetworkState::default()),
|
||||
);
|
||||
|
||||
// Check that the v0 storage is populated
|
||||
assert_eq!(v0::ReceivedHeartbeats::<T>::iter().count(), 2);
|
||||
assert_eq!(crate::ReceivedHeartbeats::<T>::iter().count(), 0, "V1 storage corrupted");
|
||||
|
||||
// Perform the migration
|
||||
let state = v1::Migration::<T>::pre_upgrade().unwrap();
|
||||
let _w = v1::Migration::<T>::on_runtime_upgrade();
|
||||
v1::Migration::<T>::post_upgrade(state).unwrap();
|
||||
|
||||
// Check that the v1 storage is populated and v0 storage is empty
|
||||
assert_eq!(v0::ReceivedHeartbeats::<T>::iter().count(), 0);
|
||||
assert_eq!(crate::ReceivedHeartbeats::<T>::iter().count(), 2);
|
||||
assert!(crate::ReceivedHeartbeats::<T>::contains_key(¤t_session, 0));
|
||||
assert_eq!(Some(true), crate::ReceivedHeartbeats::<T>::get(¤t_session, 1));
|
||||
|
||||
assert_eq!(StorageVersion::get::<Pallet<T>>(), 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -217,7 +217,6 @@ impl Config for Runtime {
|
||||
type WeightInfo = ();
|
||||
type MaxKeys = ConstU32<10_000>;
|
||||
type MaxPeerInHeartbeats = ConstU32<10_000>;
|
||||
type MaxPeerDataEncodingSize = ConstU32<1_000>;
|
||||
}
|
||||
|
||||
impl<LocalCall> frame_system::offchain::SendTransactionTypes<LocalCall> for Runtime
|
||||
|
||||
@@ -22,12 +22,9 @@
|
||||
use super::*;
|
||||
use crate::mock::*;
|
||||
use frame_support::{assert_noop, dispatch};
|
||||
use sp_core::{
|
||||
offchain::{
|
||||
testing::{TestOffchainExt, TestTransactionPoolExt},
|
||||
OffchainDbExt, OffchainWorkerExt, TransactionPoolExt,
|
||||
},
|
||||
OpaquePeerId,
|
||||
use sp_core::offchain::{
|
||||
testing::{TestOffchainExt, TestTransactionPoolExt},
|
||||
OffchainDbExt, OffchainWorkerExt, TransactionPoolExt,
|
||||
};
|
||||
use sp_runtime::{
|
||||
testing::UintAuthorityId,
|
||||
@@ -121,14 +118,8 @@ fn heartbeat(
|
||||
id: UintAuthorityId,
|
||||
validators: Vec<u64>,
|
||||
) -> dispatch::DispatchResult {
|
||||
use frame_support::unsigned::ValidateUnsigned;
|
||||
|
||||
let heartbeat = Heartbeat {
|
||||
block_number,
|
||||
network_state: OpaqueNetworkState {
|
||||
peer_id: OpaquePeerId(vec![1]),
|
||||
external_addresses: vec![],
|
||||
},
|
||||
session_index,
|
||||
authority_index,
|
||||
validators_len: validators.len() as u32,
|
||||
@@ -212,8 +203,6 @@ fn late_heartbeat_and_invalid_keys_len_should_fail() {
|
||||
|
||||
#[test]
|
||||
fn should_generate_heartbeats() {
|
||||
use frame_support::traits::OffchainWorker;
|
||||
|
||||
let mut ext = new_test_ext();
|
||||
let (offchain, _state) = TestOffchainExt::new();
|
||||
let (pool, state) = TestTransactionPoolExt::new();
|
||||
@@ -252,7 +241,6 @@ fn should_generate_heartbeats() {
|
||||
heartbeat,
|
||||
Heartbeat {
|
||||
block_number: block,
|
||||
network_state: sp_io::offchain::network_state().unwrap(),
|
||||
session_index: 2,
|
||||
authority_index: 2,
|
||||
validators_len: 3,
|
||||
@@ -365,21 +353,13 @@ fn should_not_send_a_report_if_already_online() {
|
||||
|
||||
assert_eq!(
|
||||
heartbeat,
|
||||
Heartbeat {
|
||||
block_number: 4,
|
||||
network_state: sp_io::offchain::network_state().unwrap(),
|
||||
session_index: 2,
|
||||
authority_index: 0,
|
||||
validators_len: 3,
|
||||
}
|
||||
Heartbeat { block_number: 4, session_index: 2, authority_index: 0, validators_len: 3 }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_handle_missing_progress_estimates() {
|
||||
use frame_support::traits::OffchainWorker;
|
||||
|
||||
let mut ext = new_test_ext();
|
||||
let (offchain, _state) = TestOffchainExt::new();
|
||||
let (pool, state) = TestTransactionPoolExt::new();
|
||||
|
||||
Generated
+22
-29
@@ -18,9 +18,9 @@
|
||||
//! Autogenerated weights for pallet_im_online
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! DATE: 2023-05-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
|
||||
//! HOSTNAME: `build-host`, CPU: `AMD EPYC 7601 32-Core Processor`
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
|
||||
|
||||
// Executed Command:
|
||||
@@ -37,18 +37,19 @@
|
||||
// --heap-pages=4096
|
||||
// --output=./frame/im-online/src/weights.rs
|
||||
// --header=./HEADER-APACHE2
|
||||
// --template=./.maintain/frame-weight-template.hbs
|
||||
// --template=./frame-weight-template.hbs
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use sp_std::marker::PhantomData;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for pallet_im_online.
|
||||
pub trait WeightInfo {
|
||||
fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight;
|
||||
fn validate_unsigned_and_then_heartbeat(k: u32, ) -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for pallet_im_online using the Substrate node and recommended hardware.
|
||||
@@ -61,25 +62,21 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
/// Storage: ImOnline Keys (r:1 w:0)
|
||||
/// Proof: ImOnline Keys (max_values: Some(1), max_size: Some(320002), added: 320497, mode: MaxEncodedLen)
|
||||
/// Storage: ImOnline ReceivedHeartbeats (r:1 w:1)
|
||||
/// Proof: ImOnline ReceivedHeartbeats (max_values: None, max_size: Some(10021032), added: 10023507, mode: MaxEncodedLen)
|
||||
/// Proof: ImOnline ReceivedHeartbeats (max_values: None, max_size: Some(1028), added: 3503, mode: MaxEncodedLen)
|
||||
/// Storage: ImOnline AuthoredBlocks (r:1 w:0)
|
||||
/// Proof: ImOnline AuthoredBlocks (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen)
|
||||
/// The range of component `k` is `[1, 1000]`.
|
||||
/// The range of component `e` is `[1, 100]`.
|
||||
fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight {
|
||||
fn validate_unsigned_and_then_heartbeat(k: u32, ) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `295 + k * (32 ±0)`
|
||||
// Estimated: `10024497 + e * (35 ±0) + k * (32 ±0)`
|
||||
// Minimum execution time: 95_573_000 picoseconds.
|
||||
Weight::from_parts(78_856_572, 10024497)
|
||||
// Standard Error: 315
|
||||
.saturating_add(Weight::from_parts(22_926, 0).saturating_mul(k.into()))
|
||||
// Standard Error: 3_181
|
||||
.saturating_add(Weight::from_parts(362_093, 0).saturating_mul(e.into()))
|
||||
// Estimated: `321487 + k * (1761 ±0)`
|
||||
// Minimum execution time: 120_818_000 picoseconds.
|
||||
Weight::from_parts(121_396_296, 321487)
|
||||
// Standard Error: 2_183
|
||||
.saturating_add(Weight::from_parts(58_710, 0).saturating_mul(k.into()))
|
||||
.saturating_add(T::DbWeight::get().reads(4_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(1_u64))
|
||||
.saturating_add(Weight::from_parts(0, 35).saturating_mul(e.into()))
|
||||
.saturating_add(Weight::from_parts(0, 32).saturating_mul(k.into()))
|
||||
.saturating_add(Weight::from_parts(0, 1761).saturating_mul(k.into()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,24 +89,20 @@ impl WeightInfo for () {
|
||||
/// Storage: ImOnline Keys (r:1 w:0)
|
||||
/// Proof: ImOnline Keys (max_values: Some(1), max_size: Some(320002), added: 320497, mode: MaxEncodedLen)
|
||||
/// Storage: ImOnline ReceivedHeartbeats (r:1 w:1)
|
||||
/// Proof: ImOnline ReceivedHeartbeats (max_values: None, max_size: Some(10021032), added: 10023507, mode: MaxEncodedLen)
|
||||
/// Proof: ImOnline ReceivedHeartbeats (max_values: None, max_size: Some(1028), added: 3503, mode: MaxEncodedLen)
|
||||
/// Storage: ImOnline AuthoredBlocks (r:1 w:0)
|
||||
/// Proof: ImOnline AuthoredBlocks (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen)
|
||||
/// The range of component `k` is `[1, 1000]`.
|
||||
/// The range of component `e` is `[1, 100]`.
|
||||
fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight {
|
||||
fn validate_unsigned_and_then_heartbeat(k: u32, ) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `295 + k * (32 ±0)`
|
||||
// Estimated: `10024497 + e * (35 ±0) + k * (32 ±0)`
|
||||
// Minimum execution time: 95_573_000 picoseconds.
|
||||
Weight::from_parts(78_856_572, 10024497)
|
||||
// Standard Error: 315
|
||||
.saturating_add(Weight::from_parts(22_926, 0).saturating_mul(k.into()))
|
||||
// Standard Error: 3_181
|
||||
.saturating_add(Weight::from_parts(362_093, 0).saturating_mul(e.into()))
|
||||
// Estimated: `321487 + k * (1761 ±0)`
|
||||
// Minimum execution time: 120_818_000 picoseconds.
|
||||
Weight::from_parts(121_396_296, 321487)
|
||||
// Standard Error: 2_183
|
||||
.saturating_add(Weight::from_parts(58_710, 0).saturating_mul(k.into()))
|
||||
.saturating_add(RocksDbWeight::get().reads(4_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(1_u64))
|
||||
.saturating_add(Weight::from_parts(0, 35).saturating_mul(e.into()))
|
||||
.saturating_add(Weight::from_parts(0, 32).saturating_mul(k.into()))
|
||||
.saturating_add(Weight::from_parts(0, 1761).saturating_mul(k.into()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +197,6 @@ impl pallet_im_online::Config for Test {
|
||||
type WeightInfo = ();
|
||||
type MaxKeys = ConstU32<10_000>;
|
||||
type MaxPeerInHeartbeats = ConstU32<10_000>;
|
||||
type MaxPeerDataEncodingSize = ConstU32<1_000>;
|
||||
}
|
||||
|
||||
impl pallet_offences::Config for Test {
|
||||
|
||||
Reference in New Issue
Block a user