Commits on Source (10)
......@@ -5,6 +5,12 @@ on:
- Review-Trigger
types:
- completed
workflow_dispatch:
inputs:
pr-number:
description: "Number of the PR to evaluate"
required: true
type: number
jobs:
review-approvals:
......@@ -17,6 +23,12 @@ jobs:
with:
app-id: ${{ secrets.REVIEW_APP_ID }}
private-key: ${{ secrets.REVIEW_APP_KEY }}
- name: Extract content of artifact
if: ${{ !inputs.pr-number }}
id: number
uses: Bullrich/[email protected]
with:
artifact-name: pr_number
- name: "Evaluates PR reviews and assigns reviewers"
uses: paritytech/[email protected]
with:
......@@ -24,5 +36,10 @@ jobs:
team-token: ${{ steps.app_token.outputs.token }}
checks-token: ${{ steps.app_token.outputs.token }}
# This is extracted from the triggering event
pr-number: ${{ github.event.workflow_run.pull_requests[0].number }}
pr-number: ${{ inputs.pr-number || steps.number.outputs.content }}
request-reviewers: true
- name: Log payload
if: ${{ failure() || runner.debug }}
run: echo "::debug::$payload"
env:
payload: ${{ toJson(github.event) }}
......@@ -58,3 +58,16 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
COMMENTS: ${{ steps.comments.outputs.users }}
- name: Get PR number
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
echo "Saving PR number: $PR_NUMBER"
mkdir -p ./pr
echo $PR_NUMBER > ./pr/pr_number
- uses: actions/upload-artifact@v4
name: Save PR number
with:
name: pr_number
path: pr/
retention-days: 5
......@@ -12,6 +12,4 @@ include:
# polkadot tests
- .gitlab/pipeline/zombienet/polkadot.yml
# bridges tests
# TODO: https://github.com/paritytech/parity-bridges-common/pull/2884
# commenting until we have a new relatye, compatible with updated fees scheme
# - .gitlab/pipeline/zombienet/bridges.yml
- .gitlab/pipeline/zombienet/bridges.yml
......@@ -55,9 +55,9 @@ zombienet-bridges-0001-asset-transfer-works:
- /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-new-test.sh 0001-asset-transfer --docker
- echo "Done"
zombienet-bridges-0002-mandatory-headers-synced-while-idle:
zombienet-bridges-0002-free-headers-synced-while-idle:
extends:
- .zombienet-bridges-common
script:
- /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-new-test.sh 0002-mandatory-headers-synced-while-idle --docker
- /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-new-test.sh 0002-free-headers-synced-while-idle --docker
- echo "Done"
......@@ -19,7 +19,10 @@
use async_trait::async_trait;
use structopt::StructOpt;
use relay_utils::metrics::{GlobalMetrics, StandaloneMetric};
use relay_utils::{
metrics::{GlobalMetrics, StandaloneMetric},
UniqueSaturatedInto,
};
use crate::{
cli::{bridge::*, chain_schema::*, PrometheusParams},
......@@ -48,6 +51,21 @@ pub struct RelayHeadersParams {
prometheus_params: PrometheusParams,
}
/// Single header relaying params.
#[derive(StructOpt)]
pub struct RelayHeaderParams {
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
target: TargetConnectionParams,
#[structopt(flatten)]
target_sign: TargetSigningParams,
/// Number of the source chain header that we want to relay. It must have a persistent
/// storage proof at the [`Self::source`] node, otherwise the command will fail.
#[structopt(long)]
number: u128,
}
impl RelayHeadersParams {
fn headers_to_relay(&self) -> HeadersToRelay {
match (self.only_mandatory_headers, self.only_free_headers) {
......@@ -89,4 +107,23 @@ pub trait HeadersRelayer: RelayToRelayHeadersCliBridge {
)
.await
}
/// Relay single header. No checks are made to ensure that transaction will succeed.
async fn relay_header(data: RelayHeaderParams) -> anyhow::Result<()> {
let source_client = data.source.into_client::<Self::Source>().await?;
let target_client = data.target.into_client::<Self::Target>().await?;
let target_transactions_mortality = data.target_sign.target_transactions_mortality;
let target_sign = data.target_sign.to_keypair::<Self::Target>()?;
crate::finality::relay_single_header::<Self::Finality>(
source_client,
target_client,
crate::TransactionParams {
signer: target_sign,
mortality: target_transactions_mortality,
},
data.number.unique_saturated_into(),
)
.await
}
}
......@@ -25,13 +25,15 @@ use crate::{
use async_trait::async_trait;
use bp_header_chain::justification::{GrandpaJustification, JustificationVerificationContext};
use finality_relay::{FinalityPipeline, FinalitySyncPipeline, HeadersToRelay};
use finality_relay::{
FinalityPipeline, FinalitySyncPipeline, HeadersToRelay, SourceClient, TargetClient,
};
use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig};
use relay_substrate_client::{
transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain,
ChainWithTransactions, Client, HashOf, HeaderOf, SyncHeader,
};
use relay_utils::metrics::MetricsParams;
use relay_utils::{metrics::MetricsParams, TrackedTransactionStatus, TransactionTracker};
use sp_core::Pair;
use std::{fmt::Debug, marker::PhantomData};
......@@ -274,3 +276,34 @@ pub async fn run<P: SubstrateFinalitySyncPipeline>(
.await
.map_err(|e| anyhow::format_err!("{}", e))
}
/// Relay single header. No checks are made to ensure that transaction will succeed.
pub async fn relay_single_header<P: SubstrateFinalitySyncPipeline>(
source_client: Client<P::SourceChain>,
target_client: Client<P::TargetChain>,
transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
header_number: BlockNumberOf<P::SourceChain>,
) -> anyhow::Result<()> {
let finality_source = SubstrateFinalitySource::<P>::new(source_client, None);
let (header, proof) = finality_source.header_and_finality_proof(header_number).await?;
let Some(proof) = proof else {
return Err(anyhow::format_err!(
"Unable to submit {} header #{} to {}: no finality proof",
P::SourceChain::NAME,
header_number,
P::TargetChain::NAME,
));
};
let finality_target = SubstrateFinalityTarget::<P>::new(target_client, transaction_params);
let tx_tracker = finality_target.submit_finality_proof(header, proof, false).await?;
match tx_tracker.wait().await {
TrackedTransactionStatus::Finalized(_) => Ok(()),
TrackedTransactionStatus::Lost => Err(anyhow::format_err!(
"Transaction with {} header #{} is considered lost at {}",
P::SourceChain::NAME,
header_number,
P::TargetChain::NAME,
)),
}
}
......@@ -273,8 +273,10 @@ where
},
None => {
instructions.extend(vec![
// Deposit asset to beneficiary.
DepositAsset { assets: Definite(asset.into()), beneficiary },
// Deposit both asset and fees to beneficiary so the fees will not get
// trapped. Another benefit is when fees left more than ED on AssetHub could be
// used to create the beneficiary account in case it does not exist.
DepositAsset { assets: Wild(AllCounted(2)), beneficiary },
]);
},
}
......
......@@ -10,33 +10,23 @@ async function run(nodeName, networkInfo, args) {
// start listening to new blocks
let totalGrandpaHeaders = 0;
let initialParachainHeaderImported = false;
let totalParachainHeaders = 0;
api.rpc.chain.subscribeNewHeads(async function (header) {
const apiAtParent = await api.at(header.parentHash);
const apiAtCurrent = await api.at(header.hash);
const currentEvents = await apiAtCurrent.query.system.events();
totalGrandpaHeaders += await utils.ensureOnlyMandatoryGrandpaHeadersImported(
bridgedChain,
apiAtParent,
apiAtCurrent,
currentEvents,
);
initialParachainHeaderImported = await utils.ensureOnlyInitialParachainHeaderImported(
bridgedChain,
apiAtParent,
apiAtCurrent,
currentEvents,
);
totalGrandpaHeaders += await utils.countGrandpaHeaderImports(bridgedChain, currentEvents);
totalParachainHeaders += await utils.countParachainHeaderImports(bridgedChain, currentEvents);
});
// wait given time
await new Promise(resolve => setTimeout(resolve, exitAfterSeconds * 1000));
// if we haven't seen any new GRANDPA or parachain headers => fail
if (totalGrandpaHeaders == 0) {
// if we haven't seen many (>1) new GRANDPA or parachain headers => fail
if (totalGrandpaHeaders <= 1) {
throw new Error("No bridged relay chain headers imported");
}
if (!initialParachainHeaderImported) {
if (totalParachainHeaders <= 1) {
throw new Error("No bridged parachain headers imported");
}
}
......
Description: While relayer is idle, we only sync free Rococo (and a single Rococo BH) headers to Westend BH.
Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml
Creds: config
# local chain spec gives `1u64 << 60` tokens to every endowed account: if it'll ever
# change, it'd need to be fixed here as well
# //Charlie only submits free and mandatory relay chain headers, so the balance should stay the same
bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-asset-balance.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y" return is 1152921504606846976 within 30 seconds
# //Dave only submits free parachain headers, so the balance should stay the same
bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-asset-balance.js with "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy" return is 1152921504606846976 within 30 seconds
# ensure that we have synced multiple relay and parachain headers while idle. This includes both
# headers that were generated while relay was offline and those in the next 100 seconds while script is active.
bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/multiple-headers-synced.js with "300,rococo-at-westend" within 600 seconds
# //Charlie only submits free and mandatory relay chain headers, so the balance should stay the same
bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-asset-balance.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y" return is 1152921504606846976 within 30 seconds
# //Dave only submits free parachain headers, so the balance should stay the same
bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-asset-balance.js with "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy" return is 1152921504606846976 within 30 seconds
......@@ -22,7 +22,7 @@ echo
# which is expected to be 60 seconds for the test environment.
echo -e "Sleeping 90s before starting relayer ...\n"
sleep 90
${BASH_SOURCE%/*}/../../environments/rococo-westend/start_relayer.sh $rococo_dir $westend_dir relayer_pid
${BASH_SOURCE%/*}/../../environments/rococo-westend/start_relayer.sh $rococo_dir $westend_dir finality_relayer_pid parachains_relayer_pid messages_relayer_pid
run_zndsl ${BASH_SOURCE%/*}/rococo-to-westend.zndsl $westend_dir
run_zndsl ${BASH_SOURCE%/*}/westend-to-rococo.zndsl $rococo_dir
......
Description: While relayer is idle, we only sync free Westend (and a single Westend BH) headers to Rococo BH.
Network: {{ENV_PATH}}/bridge_hub_rococo_local_network.toml
Creds: config
# local chain spec gives `1u64 << 60` tokens to every endowed account: if it'll ever
# change, it'd need to be fixed here as well
# //Charlie has inital balance
bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-asset-balance.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y" return is 1152921504606846976 within 30 seconds
# //Dave has inital balance
bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-asset-balance.js with "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy" return is 1152921504606846976 within 30 seconds
# ensure that we have synced multiple relay and parachain headers while idle. This includes both
# headers that were generated while relay was offline and those in the next 100 seconds while script is active.
bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/multiple-headers-synced.js with "300,westend-at-rococo" within 600 seconds
# //Charlie only submits free and mandatory relay chain headers, so the balance should stay the same
bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-asset-balance.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y" return is 1152921504606846976 within 30 seconds
# //Dave only submits free parachain headers, so the balance should stay the same
bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-asset-balance.js with "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy" return is 1152921504606846976 within 30 seconds
Description: While relayer is idle, we only sync mandatory Rococo (and a single Rococo BH) headers to Westend BH.
Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml
Creds: config
# ensure that relayer is only syncing mandatory headers while idle. This includes both headers that were
# generated while relay was offline and those in the next 100 seconds while script is active.
bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/only-mandatory-headers-synced-when-idle.js with "300,rococo-at-westend" within 600 seconds
Description: While relayer is idle, we only sync mandatory Westend (and a single Westend BH) headers to Rococo BH.
Network: {{ENV_PATH}}/bridge_hub_rococo_local_network.toml
Creds: config
# ensure that relayer is only syncing mandatory headers while idle. This includes both headers that were
# generated while relay was offline and those in the next 100 seconds while script is active.
bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/only-mandatory-headers-synced-when-idle.js with "300,westend-at-rococo" within 600 seconds
......@@ -27,7 +27,7 @@ use snowbridge_pallet_inbound_queue_fixtures::{
};
use snowbridge_pallet_system;
use snowbridge_router_primitives::inbound::{
Command, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage,
Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage,
};
use sp_core::H256;
use sp_runtime::{DispatchError::Token, TokenError::FundsUnavailable};
......@@ -40,6 +40,7 @@ const TREASURY_ACCOUNT: [u8; 32] =
const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d");
const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e");
const INSUFFICIENT_XCM_FEE: u128 = 1000;
const XCM_FEE: u128 = 4_000_000_000;
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
pub enum ControlCall {
......@@ -555,3 +556,133 @@ fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() {
);
});
}
fn send_token_from_ethereum_to_asset_hub_with_fee(account_id: [u8; 32], fee: u128) {
let weth_asset_location: Location = Location::new(
2,
[EthereumNetwork::get().into(), AccountKey20 { network: None, key: WETH }],
);
// (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH })
// Fund asset hub sovereign on bridge hub
let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new(
1,
[Parachain(AssetHubRococo::para_id().into())],
));
BridgeHubRococo::fund_accounts(vec![(asset_hub_sovereign.clone(), INITIAL_FUND)]);
// Register WETH
AssetHubRococo::execute_with(|| {
type RuntimeOrigin = <AssetHubRococo as Chain>::RuntimeOrigin;
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::force_create(
RuntimeOrigin::root(),
weth_asset_location.clone().try_into().unwrap(),
asset_hub_sovereign.into(),
false,
1,
));
assert!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::asset_exists(
weth_asset_location.clone().try_into().unwrap(),
));
});
// Send WETH to an existent account on asset hub
BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
type EthereumInboundQueue =
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
let message_id: H256 = [0; 32].into();
let message = VersionedMessage::V1(MessageV1 {
chain_id: CHAIN_ID,
command: Command::SendToken {
token: WETH.into(),
destination: Destination::AccountId32 { id: account_id },
amount: 1_000_000,
fee,
},
});
let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap();
assert_ok!(EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()));
// Check that the message was sent
assert_expected_events!(
BridgeHubRococo,
vec![
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
]
);
});
}
#[test]
fn send_token_from_ethereum_to_existent_account_on_asset_hub() {
send_token_from_ethereum_to_asset_hub_with_fee(AssetHubRococoSender::get().into(), XCM_FEE);
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
// Check that the token was received and issued as a foreign asset on AssetHub
assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},
]
);
});
}
#[test]
fn send_token_from_ethereum_to_non_existent_account_on_asset_hub() {
send_token_from_ethereum_to_asset_hub_with_fee([1; 32], XCM_FEE);
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
// Check that the token was received and issued as a foreign asset on AssetHub
assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},
]
);
});
}
#[test]
fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficient_fee() {
send_token_from_ethereum_to_asset_hub_with_fee([1; 32], INSUFFICIENT_XCM_FEE);
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
// Check that the message was not processed successfully due to insufficient fee
assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {},
]
);
});
}
#[test]
fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient_fee_but_do_not_satisfy_ed(
) {
// On AH the xcm fee is 33_873_024 and the ED is 3_300_000
send_token_from_ethereum_to_asset_hub_with_fee([1; 32], 36_000_000);
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
// Check that the message was not processed successfully due to insufficient ED
assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {},
]
);
});
}
# this image is built on top of existing Zombienet image
ARG ZOMBIENET_IMAGE
# this image uses substrate-relay image built elsewhere
ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.2.1
ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.5.0
# metadata
ARG VCS_REF
......
......@@ -64,7 +64,7 @@ use sp_runtime::{
KeyTypeId, Perbill,
};
use sp_session::{GetSessionNumber, GetValidatorCount};
use sp_staking::offence::{DisableStrategy, Kind, Offence, OffenceError, ReportOffence};
use sp_staking::offence::{Kind, Offence, OffenceError, ReportOffence};
use sp_std::{
collections::{btree_map::Entry, btree_set::BTreeSet},
prelude::*,
......@@ -134,15 +134,6 @@ where
self.time_slot.clone()
}
fn disable_strategy(&self) -> DisableStrategy {
match self.kind {
SlashingOffenceKind::ForInvalid => DisableStrategy::Always,
// in the future we might change it based on number of disputes initiated:
// <https://github.com/paritytech/polkadot/issues/5946>
SlashingOffenceKind::AgainstValid => DisableStrategy::Never,
}
}
fn slash_fraction(&self, _offenders: u32) -> Perbill {
self.slash_fraction
}
......
......@@ -313,7 +313,6 @@ parameter_types! {
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
pub const MaxExposurePageSize: u32 = 64;
pub const MaxNominators: u32 = 256;
pub storage OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const MaxAuthorities: u32 = 100_000;
pub const OnChainMaxWinners: u32 = u32::MAX;
// Unbounded number of election targets and voters.
......@@ -349,7 +348,6 @@ impl pallet_staking::Config for Runtime {
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = MaxExposurePageSize;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
......@@ -364,6 +362,7 @@ impl pallet_staking::Config for Runtime {
type BenchmarkingConfig = runtime_common::StakingBenchmarkingConfig;
type EventListeners = ();
type WeightInfo = ();
type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
}
parameter_types! {
......
......@@ -613,7 +613,6 @@ parameter_types! {
// this is an unbounded number. We just set it to a reasonably high value, 1 full page
// of nominators.
pub const MaxNominators: u32 = 64;
pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const MaxNominations: u32 = <NposCompactSolution16 as frame_election_provider_support::NposSolution>::LIMIT as u32;
pub const MaxControllersInDeprecationBatch: u32 = 751;
}
......@@ -634,7 +633,6 @@ impl pallet_staking::Config for Runtime {
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = MaxExposurePageSize;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = ElectionProviderMultiPhase;
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
......@@ -647,6 +645,7 @@ impl pallet_staking::Config for Runtime {
type BenchmarkingConfig = runtime_common::StakingBenchmarkingConfig;
type EventListeners = NominationPools;
type WeightInfo = weights::pallet_staking::WeightInfo<Runtime>;
type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
}
impl pallet_fast_unstake::Config for Runtime {
......@@ -1649,7 +1648,7 @@ pub mod migrations {
}
/// Unreleased migrations. Add new ones here:
pub type Unreleased = ();
pub type Unreleased = (pallet_staking::migrations::v15::MigrateV14ToV15<Runtime>,);
}
/// Unchecked extrinsic type as expected by this runtime.
......
......@@ -21,7 +21,7 @@ requests = { memory = "2G", cpu = "1" }
[[relaychain.node_groups]]
name = "honest-validator"
count = 3
args = ["-lparachain=debug"]
args = ["-lparachain=debug,runtime::staking=debug"]
[[relaychain.node_groups]]
image = "{{MALUS_IMAGE}}"
......
title: Validator disabling strategy in runtime
doc:
- audience: Node Operator
description: |
On each committed offence (no matter slashable or not) the offending validator will be
disabled for a whole era.
- audience: Runtime Dev
description: |
The disabling strategy in staking pallet is no longer hardcoded but abstracted away via
`DisablingStrategy` trait. The trait contains a single function (make_disabling_decision) which
is called for each offence. The function makes a decision if (and which) validators should be
disabled. A default implementation is provided - `UpToLimitDisablingStrategy`. It
will be used on Kusama and Polkadot. In nutshell `UpToLimitDisablingStrategy`
disables offenders up to the configured threshold. Offending validators are not disabled for
offences in previous eras. The threshold is controlled via `DISABLING_LIMIT_FACTOR` (a generic
parameter of `UpToLimitDisablingStrategy`).
migrations:
db: []
runtime:
- reference: pallet-staking
description: |
Renames `OffendingValidators` storage item to `DisabledValidators` and changes its type from
`Vec<(u32, bool)>` to `Vec<u32>`.
crates:
- name: pallet-staking
\ No newline at end of file