mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 18:41:03 +00:00
disputes pallet: Remove spam slots (#6345)
* disputes pallet: Filter disputes with votes less than supermajority threshold * Remove `max_spam_slots` usages * Remove `SpamSlots` * Remove `SpamSlotChange` * Remove `Error<T>::PotentialSpam` and stale comments * `create_disputes_with_no_spam` -> `create_disputes` * Make tests compile - wip commit * Rework `test_dispute_timeout`. Rename `update_spam_slots` to `filter_dispute_set` * Remove `dispute_statement_becoming_onesided_due_to_spamslots_is_accepted` and `filter_correctly_accounts_spam_slots` -> they bring no value with removed spam slots * Fix `test_provide_multi_dispute_success_and_other` * Remove an old comment * Remove spam slots from tests - clean todo comments * Remove test - `test_decrement_spam` * todo comments * Update TODO comments * Extract `test_unconfirmed_are_ignored` as separate test case * Remove dead code * Fix `test_unconfirmed_are_ignored` * Remove dead code in `filter_dispute_data` * Fix weights (related to commit "Remove `SpamSlots`") * Disputes migration - first try * Remove `dispute_max_spam_slots` + storage migration * Fix `HostConfig` migration tests * Deprecate `SpamSlots` * Code review feedback * add weight for storage version update * fix bound for clear() * Fix weights in disputes migration * Revert "Deprecate `SpamSlots`" This reverts commit 8c4d967c7b061abd76ba8b551223918c0b9e6370. * Make mod migration public * Remove `SpamSlots` from disputes pallet and use `storage_alias` in the migration * Fix call to `clear()` for `SpamSlots` in migration * Update migration and add a `try-runtime` test * Add `pre_upgrade` `try-runtime` test * Fix some test names in `HostConfiguration` migration * Link spamslots migration in all runtimes * Add `test_unconfirmed_disputes_cause_block_import_error` * Update guide - Remove `SpamSlots` related information from roadmap/implementers-guide/src/runtime/disputes.md - Add 'Disputes filtering' to Runtime section of the Implementor's guide * Update runtime/parachains/src/configuration/migration.rs Co-authored-by: Marcin S. <marcin@bytedude.com> * Code review feedback - update logs * Code review feedback: fix weights * Update runtime/parachains/src/disputes.rs Co-authored-by: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com> * Additional logs in disputes migration * Fix merge conflicts * Add version checks in try-runtime tests * Fix a compilation warning` Co-authored-by: Marcin S. <marcin@bytedude.com> Co-authored-by: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
715e98268a
commit
ed9a1a400e
@@ -37,12 +37,6 @@ Disputes: double_map (SessionIndex, CandidateHash) -> Option<DisputeState>,
|
|||||||
// All included blocks on the chain, as well as the block number in this chain that
|
// All included blocks on the chain, as well as the block number in this chain that
|
||||||
// should be reverted back to if the candidate is disputed and determined to be invalid.
|
// should be reverted back to if the candidate is disputed and determined to be invalid.
|
||||||
Included: double_map (SessionIndex, CandidateHash) -> Option<BlockNumber>,
|
Included: double_map (SessionIndex, CandidateHash) -> Option<BlockNumber>,
|
||||||
// Maps session indices to a vector indicating the number of potentially-spam disputes
|
|
||||||
// each validator is participating in. Potentially-spam disputes are remote disputes which have
|
|
||||||
// fewer than `byzantine_threshold + 1` validators.
|
|
||||||
//
|
|
||||||
// The i'th entry of the vector corresponds to the i'th validator in the session.
|
|
||||||
SpamSlots: map SessionIndex -> Option<Vec<u32>>,
|
|
||||||
// Whether the chain is frozen or not. Starts as `None`. When this is `Some`,
|
// Whether the chain is frozen or not. Starts as `None`. When this is `Some`,
|
||||||
// the chain will not accept any new parachain blocks for backing or inclusion,
|
// the chain will not accept any new parachain blocks for backing or inclusion,
|
||||||
// and its value indicates the last valid block number in the chain.
|
// and its value indicates the last valid block number in the chain.
|
||||||
@@ -58,47 +52,42 @@ Frozen: Option<BlockNumber>,
|
|||||||
1. Set `pruning_target = current_session - config.dispute_period - 1`. We add the extra `1` because we want to keep things for `config.dispute_period` _full_ sessions.
|
1. Set `pruning_target = current_session - config.dispute_period - 1`. We add the extra `1` because we want to keep things for `config.dispute_period` _full_ sessions.
|
||||||
The stuff at the end of the most recent session has been around for a little over 0 sessions, not a little over 1.
|
The stuff at the end of the most recent session has been around for a little over 0 sessions, not a little over 1.
|
||||||
1. If `LastPrunedSession` is `None`, then set `LastPrunedSession` to `Some(pruning_target)` and return.
|
1. If `LastPrunedSession` is `None`, then set `LastPrunedSession` to `Some(pruning_target)` and return.
|
||||||
1. Otherwise, clear out all disputes, included candidates, and `SpamSlots` entries in the range `last_pruned..=pruning_target` and set `LastPrunedSession` to `Some(pruning_target)`.
|
2. Otherwise, clear out all disputes and included candidates entries in the range `last_pruned..=pruning_target` and set `LastPrunedSession` to `Some(pruning_target)`.
|
||||||
|
|
||||||
## Block Initialization
|
## Block Initialization
|
||||||
|
|
||||||
1. Iterate through all disputes. If any have not concluded and started more than `config.dispute_conclusion_by_timeout_period` blocks ago, set them to `Concluded` and mildly punish all validators associated, as they have failed to distribute available data. If the `Included` map does not contain the candidate and there are fewer than `byzantine_threshold + 1` participating validators, reduce `SpamSlots` for all participating validators.
|
1. Iterate through all disputes. If any have not concluded and started more than `config.dispute_conclusion_by_timeout_period` blocks ago, set them to `Concluded` and mildly punish all validators associated, as they have failed to distribute available data.
|
||||||
|
|
||||||
## Routines
|
## Routines
|
||||||
|
|
||||||
* `filter_multi_dispute_data(MultiDisputeStatementSet) -> MultiDisputeStatementSet`:
|
* `filter_multi_dispute_data(MultiDisputeStatementSet) -> MultiDisputeStatementSet`:
|
||||||
1. Takes a `MultiDisputeStatementSet` and filters it down to a `MultiDisputeStatementSet`
|
1. Takes a `MultiDisputeStatementSet` and filters it down to a `MultiDisputeStatementSet`
|
||||||
that satisfies all the criteria of `provide_multi_dispute_data`. That is, eliminating
|
that satisfies all the criteria of `provide_multi_dispute_data`. That is, eliminating
|
||||||
ancient votes, votes which overwhelm the maximum amount of spam slots, and duplicates.
|
ancient votes, duplicates and unconfirmed disputes.
|
||||||
This can be used by block authors to create the final submission in a block which is
|
This can be used by block authors to create the final submission in a block which is
|
||||||
guaranteed to pass the `provide_multi_dispute_data` checks.
|
guaranteed to pass the `provide_multi_dispute_data` checks.
|
||||||
|
|
||||||
* `provide_multi_dispute_data(MultiDisputeStatementSet) -> Vec<(SessionIndex, Hash)>`:
|
* `provide_multi_dispute_data(MultiDisputeStatementSet) -> Vec<(SessionIndex, Hash)>`:
|
||||||
1. Pass on each dispute statement set to `provide_dispute_data`, propagating failure.
|
1. Pass on each dispute statement set to `provide_dispute_data`, propagating failure.
|
||||||
1. Return a list of all candidates who just had disputes initiated.
|
2. Return a list of all candidates who just had disputes initiated.
|
||||||
|
|
||||||
* `provide_dispute_data(DisputeStatementSet) -> bool`: Provide data to an ongoing dispute or initiate a dispute.
|
* `provide_dispute_data(DisputeStatementSet) -> bool`: Provide data to an ongoing dispute or initiate a dispute.
|
||||||
1. All statements must be issued under the correct session for the correct candidate.
|
1. All statements must be issued under the correct session for the correct candidate.
|
||||||
1. `SessionInfo` is used to check statement signatures and this function should fail if any signatures are invalid.
|
1. `SessionInfo` is used to check statement signatures and this function should fail if any signatures are invalid.
|
||||||
1. If there is no dispute under `Disputes`, create a new `DisputeState` with blank bitfields.
|
1. If there is no dispute under `Disputes`, create a new `DisputeState` with blank bitfields.
|
||||||
1. If `concluded_at` is `Some`, and is `concluded_at + config.post_conclusion_acceptance_period < now`, return false.
|
1. If `concluded_at` is `Some`, and is `concluded_at + config.post_conclusion_acceptance_period < now`, return false.
|
||||||
1. If the overlap of the validators in the `DisputeStatementSet` and those already present in the `DisputeState` is fewer in number than `byzantine_threshold + 1` and the candidate is not present in the `Included` map
|
2. Import all statements into the dispute. This should fail if any statements are duplicate or if the corresponding bit for the corresponding validator is set in the dispute already.
|
||||||
1. increment `SpamSlots` for each validator in the `DisputeStatementSet` which is not already in the `DisputeState`. Initialize the `SpamSlots` to a zeroed vector first, if necessary. do not increment `SpamSlots` if the candidate is local.
|
3. If `concluded_at` is `None`, reward all statements.
|
||||||
1. If the value for any spam slot exceeds `config.dispute_max_spam_slots`, return false.
|
4. If `concluded_at` is `Some`, reward all statements slightly less.
|
||||||
1. If the overlap of the validators in the `DisputeStatementSet` and those already present in the `DisputeState` is at least `byzantine_threshold + 1`, the `DisputeState` has fewer than `byzantine_threshold + 1` validators, and the candidate is not present in the `Included` map, then decrease `SpamSlots` by 1 for each validator in the `DisputeState`.
|
5. If either side now has supermajority and did not previously, slash the other side. This may be both sides, and we support this possibility in code, but note that this requires validators to participate on both sides which has negative expected value. Set `concluded_at` to `Some(now)` if it was `None`.
|
||||||
1. Import all statements into the dispute. This should fail if any statements are duplicate or if the corresponding bit for the corresponding validator is set in the dispute already.
|
6. If just concluded against the candidate and the `Included` map contains `(session, candidate)`: invoke `revert_and_freeze` with the stored block number.
|
||||||
1. If `concluded_at` is `None`, reward all statements.
|
7. Return true if just initiated, false otherwise.
|
||||||
1. If `concluded_at` is `Some`, reward all statements slightly less.
|
|
||||||
1. If either side now has supermajority and did not previously, slash the other side. This may be both sides, and we support this possibility in code, but note that this requires validators to participate on both sides which has negative expected value. Set `concluded_at` to `Some(now)` if it was `None`.
|
|
||||||
1. If just concluded against the candidate and the `Included` map contains `(session, candidate)`: invoke `revert_and_freeze` with the stored block number.
|
|
||||||
1. Return true if just initiated, false otherwise.
|
|
||||||
|
|
||||||
* `disputes() -> Vec<(SessionIndex, CandidateHash, DisputeState)>`: Get a list of all disputes and info about dispute state.
|
* `disputes() -> Vec<(SessionIndex, CandidateHash, DisputeState)>`: Get a list of all disputes and info about dispute state.
|
||||||
1. Iterate over all disputes in `Disputes` and collect into a vector.
|
1. Iterate over all disputes in `Disputes` and collect into a vector.
|
||||||
|
|
||||||
* `note_included(SessionIndex, CandidateHash, included_in: BlockNumber)`:
|
* `note_included(SessionIndex, CandidateHash, included_in: BlockNumber)`:
|
||||||
1. Add `(SessionIndex, CandidateHash)` to the `Included` map with `included_in - 1` as the value.
|
1. Add `(SessionIndex, CandidateHash)` to the `Included` map with `included_in - 1` as the value.
|
||||||
1. If there is a dispute under `(Sessionindex, CandidateHash)` with fewer than `byzantine_threshold + 1` participating validators, decrease `SpamSlots` by 1 for each validator in the `DisputeState`.
|
|
||||||
1. If there is a dispute under `(SessionIndex, CandidateHash)` that has concluded against the candidate, invoke `revert_and_freeze` with the stored block number.
|
1. If there is a dispute under `(SessionIndex, CandidateHash)` that has concluded against the candidate, invoke `revert_and_freeze` with the stored block number.
|
||||||
|
|
||||||
* `concluded_invalid(SessionIndex, CandidateHash) -> bool`: Returns whether a candidate has already concluded a dispute in the negative.
|
* `concluded_invalid(SessionIndex, CandidateHash) -> bool`: Returns whether a candidate has already concluded a dispute in the negative.
|
||||||
@@ -111,3 +100,27 @@ Frozen: Option<BlockNumber>,
|
|||||||
1. If `is_frozen()` return.
|
1. If `is_frozen()` return.
|
||||||
1. Set `Frozen` to `Some(BlockNumber)` to indicate a rollback to the block number.
|
1. Set `Frozen` to `Some(BlockNumber)` to indicate a rollback to the block number.
|
||||||
1. Issue a `Revert(BlockNumber + 1)` log to indicate a rollback of the block's child in the header chain, which is the same as a rollback to the block number.
|
1. Issue a `Revert(BlockNumber + 1)` log to indicate a rollback of the block's child in the header chain, which is the same as a rollback to the block number.
|
||||||
|
|
||||||
|
# Disputes filtering
|
||||||
|
|
||||||
|
All disputes delivered to the runtime by the client are filtered before the actual import. In this context actual import
|
||||||
|
means persisted in the runtime storage. The filtering has got two purposes:
|
||||||
|
- Limit the amount of data saved onchain.
|
||||||
|
- Prevent persisting malicious dispute data onchain.
|
||||||
|
|
||||||
|
*Implementation note*: Filtering is performed in function `filter_dispute_data` from `Disputes` pallet.
|
||||||
|
|
||||||
|
The filtering is performed on the whole statement set which is about to be imported onchain. The following filters are
|
||||||
|
applied:
|
||||||
|
1. Remove ancient disputes - if a dispute is concluded before the block number indicated in `OLDEST_ACCEPTED` parameter
|
||||||
|
it is removed from the set. `OLDEST_ACCEPTED` is a runtime configuration option.
|
||||||
|
*Implementation note*: `dispute_post_conclusion_acceptance_period` from
|
||||||
|
`HostConfiguration` is used in the current Polkadot/Kusama implementation.
|
||||||
|
2. Remove votes from unknown validators. If there is a vote from a validator which wasn't an authority in the session
|
||||||
|
where the dispute was raised - they are removed. Please note that this step removes only single votes instead of
|
||||||
|
removing the whole dispute.
|
||||||
|
3. Remove one sided disputes - if a dispute doesn't contain two opposing votes it is not imported onchain. This serves
|
||||||
|
as a measure not to import one sided disputes. A dispute is raised only if there are two opposing votes so if the
|
||||||
|
client is not sending them the dispute is a potential spam.
|
||||||
|
4. Remove unconfirmed disputes - if a dispute contains less votes than the byzantine threshold it is removed. This is
|
||||||
|
also a spam precaution. A legitimate client will send only confirmed disputes to the runtime.
|
||||||
@@ -1493,6 +1493,8 @@ pub type Migrations = (
|
|||||||
>,
|
>,
|
||||||
pallet_scheduler::migration::v4::CleanupAgendas<Runtime>,
|
pallet_scheduler::migration::v4::CleanupAgendas<Runtime>,
|
||||||
pallet_staking::migrations::v13::MigrateToV13<Runtime>,
|
pallet_staking::migrations::v13::MigrateToV13<Runtime>,
|
||||||
|
parachains_disputes::migration::v1::MigrateToV1<Runtime>,
|
||||||
|
parachains_configuration::migration::v4::MigrateToV4<Runtime>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Unchecked extrinsic type as expected by this runtime.
|
/// Unchecked extrinsic type as expected by this runtime.
|
||||||
|
|||||||
@@ -575,7 +575,7 @@ impl<T: paras_inherent::Config> BenchBuilder<T> {
|
|||||||
|
|
||||||
/// Fill cores `start..last` with dispute statement sets. The statement sets will have 3/4th of
|
/// Fill cores `start..last` with dispute statement sets. The statement sets will have 3/4th of
|
||||||
/// votes be valid, and 1/4th of votes be invalid.
|
/// votes be valid, and 1/4th of votes be invalid.
|
||||||
fn create_disputes_with_no_spam(
|
fn create_disputes(
|
||||||
&self,
|
&self,
|
||||||
start: u32,
|
start: u32,
|
||||||
last: u32,
|
last: u32,
|
||||||
@@ -664,7 +664,7 @@ impl<T: paras_inherent::Config> BenchBuilder<T> {
|
|||||||
let backed_candidates = builder
|
let backed_candidates = builder
|
||||||
.create_backed_candidates(&builder.backed_and_concluding_cores, builder.code_upgrade);
|
.create_backed_candidates(&builder.backed_and_concluding_cores, builder.code_upgrade);
|
||||||
|
|
||||||
let disputes = builder.create_disputes_with_no_spam(
|
let disputes = builder.create_disputes(
|
||||||
builder.backed_and_concluding_cores.len() as u32,
|
builder.backed_and_concluding_cores.len() as u32,
|
||||||
used_cores,
|
used_cores,
|
||||||
builder.dispute_sessions.as_slice(),
|
builder.dispute_sessions.as_slice(),
|
||||||
|
|||||||
@@ -192,8 +192,6 @@ pub struct HostConfiguration<BlockNumber> {
|
|||||||
pub dispute_period: SessionIndex,
|
pub dispute_period: SessionIndex,
|
||||||
/// How long after dispute conclusion to accept statements.
|
/// How long after dispute conclusion to accept statements.
|
||||||
pub dispute_post_conclusion_acceptance_period: BlockNumber,
|
pub dispute_post_conclusion_acceptance_period: BlockNumber,
|
||||||
/// The maximum number of dispute spam slots
|
|
||||||
pub dispute_max_spam_slots: u32,
|
|
||||||
/// How long it takes for a dispute to conclude by time-out, if no supermajority is reached.
|
/// How long it takes for a dispute to conclude by time-out, if no supermajority is reached.
|
||||||
pub dispute_conclusion_by_time_out_period: BlockNumber,
|
pub dispute_conclusion_by_time_out_period: BlockNumber,
|
||||||
/// The amount of consensus slots that must pass between submitting an assignment and
|
/// The amount of consensus slots that must pass between submitting an assignment and
|
||||||
@@ -263,7 +261,6 @@ impl<BlockNumber: Default + From<u32>> Default for HostConfiguration<BlockNumber
|
|||||||
max_validators: None,
|
max_validators: None,
|
||||||
dispute_period: 6,
|
dispute_period: 6,
|
||||||
dispute_post_conclusion_acceptance_period: 100.into(),
|
dispute_post_conclusion_acceptance_period: 100.into(),
|
||||||
dispute_max_spam_slots: 2,
|
|
||||||
dispute_conclusion_by_time_out_period: 200.into(),
|
dispute_conclusion_by_time_out_period: 200.into(),
|
||||||
n_delay_tranches: Default::default(),
|
n_delay_tranches: Default::default(),
|
||||||
zeroth_delay_tranche_width: Default::default(),
|
zeroth_delay_tranche_width: Default::default(),
|
||||||
@@ -752,19 +749,6 @@ pub mod pallet {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum number of dispute spam slots.
|
|
||||||
#[pallet::call_index(16)]
|
|
||||||
#[pallet::weight((
|
|
||||||
T::WeightInfo::set_config_with_u32(),
|
|
||||||
DispatchClass::Operational,
|
|
||||||
))]
|
|
||||||
pub fn set_dispute_max_spam_slots(origin: OriginFor<T>, new: u32) -> DispatchResult {
|
|
||||||
ensure_root(origin)?;
|
|
||||||
Self::schedule_config_update(|config| {
|
|
||||||
config.dispute_max_spam_slots = new;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the dispute conclusion by time out period.
|
/// Set the dispute conclusion by time out period.
|
||||||
#[pallet::call_index(17)]
|
#[pallet::call_index(17)]
|
||||||
#[pallet::weight((
|
#[pallet::weight((
|
||||||
|
|||||||
@@ -17,11 +17,7 @@
|
|||||||
//! A module that is responsible for migration of storage.
|
//! A module that is responsible for migration of storage.
|
||||||
|
|
||||||
use crate::configuration::{self, Config, Pallet, Store, MAX_POV_SIZE};
|
use crate::configuration::{self, Config, Pallet, Store, MAX_POV_SIZE};
|
||||||
use frame_support::{
|
use frame_support::{pallet_prelude::*, traits::StorageVersion, weights::Weight};
|
||||||
pallet_prelude::*,
|
|
||||||
traits::StorageVersion,
|
|
||||||
weights::{OldWeight, Weight},
|
|
||||||
};
|
|
||||||
use frame_system::pallet_prelude::BlockNumberFor;
|
use frame_system::pallet_prelude::BlockNumberFor;
|
||||||
|
|
||||||
/// The current storage version.
|
/// The current storage version.
|
||||||
@@ -29,12 +25,15 @@ use frame_system::pallet_prelude::BlockNumberFor;
|
|||||||
/// v0-v1: <https://github.com/paritytech/polkadot/pull/3575>
|
/// v0-v1: <https://github.com/paritytech/polkadot/pull/3575>
|
||||||
/// v1-v2: <https://github.com/paritytech/polkadot/pull/4420>
|
/// v1-v2: <https://github.com/paritytech/polkadot/pull/4420>
|
||||||
/// v2-v3: <https://github.com/paritytech/polkadot/pull/6091>
|
/// v2-v3: <https://github.com/paritytech/polkadot/pull/6091>
|
||||||
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);
|
/// v3-v4: <https://github.com/paritytech/polkadot/pull/6345>
|
||||||
|
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(4);
|
||||||
|
|
||||||
pub mod v3 {
|
pub mod v4 {
|
||||||
use super::*;
|
use super::*;
|
||||||
use frame_support::traits::OnRuntimeUpgrade;
|
use frame_support::{traits::OnRuntimeUpgrade, weights::constants::WEIGHT_REF_TIME_PER_MILLIS};
|
||||||
use primitives::v2::{Balance, SessionIndex};
|
use primitives::v2::{Balance, SessionIndex};
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
use sp_std::prelude::*;
|
||||||
|
|
||||||
// Copied over from configuration.rs @ de9e147695b9f1be8bd44e07861a31e483c8343a and removed
|
// Copied over from configuration.rs @ de9e147695b9f1be8bd44e07861a31e483c8343a and removed
|
||||||
// all the comments, and changed the Weight struct to OldWeight
|
// all the comments, and changed the Weight struct to OldWeight
|
||||||
@@ -51,7 +50,7 @@ pub mod v3 {
|
|||||||
pub validation_upgrade_delay: BlockNumber,
|
pub validation_upgrade_delay: BlockNumber,
|
||||||
pub max_pov_size: u32,
|
pub max_pov_size: u32,
|
||||||
pub max_downward_message_size: u32,
|
pub max_downward_message_size: u32,
|
||||||
pub ump_service_total_weight: OldWeight,
|
pub ump_service_total_weight: Weight,
|
||||||
pub hrmp_max_parachain_outbound_channels: u32,
|
pub hrmp_max_parachain_outbound_channels: u32,
|
||||||
pub hrmp_max_parathread_outbound_channels: u32,
|
pub hrmp_max_parathread_outbound_channels: u32,
|
||||||
pub hrmp_sender_deposit: Balance,
|
pub hrmp_sender_deposit: Balance,
|
||||||
@@ -79,7 +78,7 @@ pub mod v3 {
|
|||||||
pub zeroth_delay_tranche_width: u32,
|
pub zeroth_delay_tranche_width: u32,
|
||||||
pub needed_approvals: u32,
|
pub needed_approvals: u32,
|
||||||
pub relay_vrf_modulo_samples: u32,
|
pub relay_vrf_modulo_samples: u32,
|
||||||
pub ump_max_individual_weight: OldWeight,
|
pub ump_max_individual_weight: Weight,
|
||||||
pub pvf_checking_enabled: bool,
|
pub pvf_checking_enabled: bool,
|
||||||
pub pvf_voting_ttl: SessionIndex,
|
pub pvf_voting_ttl: SessionIndex,
|
||||||
pub minimum_validation_upgrade_delay: BlockNumber,
|
pub minimum_validation_upgrade_delay: BlockNumber,
|
||||||
@@ -114,7 +113,7 @@ pub mod v3 {
|
|||||||
max_upward_queue_count: Default::default(),
|
max_upward_queue_count: Default::default(),
|
||||||
max_upward_queue_size: Default::default(),
|
max_upward_queue_size: Default::default(),
|
||||||
max_downward_message_size: Default::default(),
|
max_downward_message_size: Default::default(),
|
||||||
ump_service_total_weight: OldWeight(Default::default()),
|
ump_service_total_weight: Default::default(),
|
||||||
max_upward_message_size: Default::default(),
|
max_upward_message_size: Default::default(),
|
||||||
max_upward_message_num_per_candidate: Default::default(),
|
max_upward_message_num_per_candidate: Default::default(),
|
||||||
hrmp_sender_deposit: Default::default(),
|
hrmp_sender_deposit: Default::default(),
|
||||||
@@ -127,8 +126,9 @@ pub mod v3 {
|
|||||||
hrmp_max_parachain_outbound_channels: Default::default(),
|
hrmp_max_parachain_outbound_channels: Default::default(),
|
||||||
hrmp_max_parathread_outbound_channels: Default::default(),
|
hrmp_max_parathread_outbound_channels: Default::default(),
|
||||||
hrmp_max_message_num_per_candidate: Default::default(),
|
hrmp_max_message_num_per_candidate: Default::default(),
|
||||||
ump_max_individual_weight: OldWeight(
|
ump_max_individual_weight: Weight::from_parts(
|
||||||
frame_support::weights::constants::WEIGHT_REF_TIME_PER_MILLIS * 20,
|
20u64 * WEIGHT_REF_TIME_PER_MILLIS,
|
||||||
|
MAX_POV_SIZE as u64,
|
||||||
),
|
),
|
||||||
pvf_checking_enabled: false,
|
pvf_checking_enabled: false,
|
||||||
pvf_voting_ttl: 2u32.into(),
|
pvf_voting_ttl: 2u32.into(),
|
||||||
@@ -137,32 +137,51 @@ pub mod v3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MigrateToV3<T>(sp_std::marker::PhantomData<T>);
|
pub struct MigrateToV4<T>(sp_std::marker::PhantomData<T>);
|
||||||
impl<T: Config> OnRuntimeUpgrade for MigrateToV3<T> {
|
impl<T: Config> OnRuntimeUpgrade for MigrateToV4<T> {
|
||||||
fn on_runtime_upgrade() -> Weight {
|
#[cfg(feature = "try-runtime")]
|
||||||
if StorageVersion::get::<Pallet<T>>() == 2 {
|
fn pre_upgrade() -> Result<Vec<u8>, &'static str> {
|
||||||
let weight_consumed = migrate_to_v3::<T>();
|
log::trace!(target: crate::configuration::LOG_TARGET, "Running pre_upgrade()");
|
||||||
|
|
||||||
log::info!(target: configuration::LOG_TARGET, "MigrateToV3 executed successfully");
|
ensure!(StorageVersion::get::<Pallet<T>>() == 3, "The migration requires version 3");
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_runtime_upgrade() -> Weight {
|
||||||
|
if StorageVersion::get::<Pallet<T>>() == 3 {
|
||||||
|
let weight_consumed = migrate_to_v4::<T>();
|
||||||
|
|
||||||
|
log::info!(target: configuration::LOG_TARGET, "MigrateToV4 executed successfully");
|
||||||
STORAGE_VERSION.put::<Pallet<T>>();
|
STORAGE_VERSION.put::<Pallet<T>>();
|
||||||
|
|
||||||
weight_consumed
|
weight_consumed
|
||||||
} else {
|
} else {
|
||||||
log::warn!(target: configuration::LOG_TARGET, "MigrateToV3 should be removed.");
|
log::warn!(target: configuration::LOG_TARGET, "MigrateToV4 should be removed.");
|
||||||
T::DbWeight::get().reads(1)
|
T::DbWeight::get().reads(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> {
|
||||||
|
log::trace!(target: crate::configuration::LOG_TARGET, "Running post_upgrade()");
|
||||||
|
ensure!(
|
||||||
|
StorageVersion::get::<Pallet<T>>() == STORAGE_VERSION,
|
||||||
|
"Storage version should be 4 after the migration"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn migrate_to_v3<T: Config>() -> Weight {
|
fn migrate_to_v4<T: Config>() -> Weight {
|
||||||
// Unusual formatting is justified:
|
// Unusual formatting is justified:
|
||||||
// - make it easier to verify that fields assign what they supposed to assign.
|
// - make it easier to verify that fields assign what they supposed to assign.
|
||||||
// - this code is transient and will be removed after all migrations are done.
|
// - this code is transient and will be removed after all migrations are done.
|
||||||
// - this code is important enough to optimize for legibility sacrificing consistency.
|
// - this code is important enough to optimize for legibility sacrificing consistency.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let translate =
|
let translate =
|
||||||
|pre: v3::OldHostConfiguration<BlockNumberFor<T>>| ->
|
|pre: v4::OldHostConfiguration<BlockNumberFor<T>>| ->
|
||||||
configuration::HostConfiguration<BlockNumberFor<T>>
|
configuration::HostConfiguration<BlockNumberFor<T>>
|
||||||
{
|
{
|
||||||
super::HostConfiguration {
|
super::HostConfiguration {
|
||||||
@@ -177,6 +196,7 @@ validation_upgrade_cooldown : pre.validation_upgrade_cooldown,
|
|||||||
validation_upgrade_delay : pre.validation_upgrade_delay,
|
validation_upgrade_delay : pre.validation_upgrade_delay,
|
||||||
max_pov_size : pre.max_pov_size,
|
max_pov_size : pre.max_pov_size,
|
||||||
max_downward_message_size : pre.max_downward_message_size,
|
max_downward_message_size : pre.max_downward_message_size,
|
||||||
|
ump_service_total_weight : pre.ump_service_total_weight,
|
||||||
hrmp_max_parachain_outbound_channels : pre.hrmp_max_parachain_outbound_channels,
|
hrmp_max_parachain_outbound_channels : pre.hrmp_max_parachain_outbound_channels,
|
||||||
hrmp_max_parathread_outbound_channels : pre.hrmp_max_parathread_outbound_channels,
|
hrmp_max_parathread_outbound_channels : pre.hrmp_max_parathread_outbound_channels,
|
||||||
hrmp_sender_deposit : pre.hrmp_sender_deposit,
|
hrmp_sender_deposit : pre.hrmp_sender_deposit,
|
||||||
@@ -197,19 +217,17 @@ max_validators_per_core : pre.max_validators_per_core,
|
|||||||
max_validators : pre.max_validators,
|
max_validators : pre.max_validators,
|
||||||
dispute_period : pre.dispute_period,
|
dispute_period : pre.dispute_period,
|
||||||
dispute_post_conclusion_acceptance_period: pre.dispute_post_conclusion_acceptance_period,
|
dispute_post_conclusion_acceptance_period: pre.dispute_post_conclusion_acceptance_period,
|
||||||
dispute_max_spam_slots : pre.dispute_max_spam_slots,
|
|
||||||
dispute_conclusion_by_time_out_period : pre.dispute_conclusion_by_time_out_period,
|
dispute_conclusion_by_time_out_period : pre.dispute_conclusion_by_time_out_period,
|
||||||
no_show_slots : pre.no_show_slots,
|
no_show_slots : pre.no_show_slots,
|
||||||
n_delay_tranches : pre.n_delay_tranches,
|
n_delay_tranches : pre.n_delay_tranches,
|
||||||
zeroth_delay_tranche_width : pre.zeroth_delay_tranche_width,
|
zeroth_delay_tranche_width : pre.zeroth_delay_tranche_width,
|
||||||
needed_approvals : pre.needed_approvals,
|
needed_approvals : pre.needed_approvals,
|
||||||
relay_vrf_modulo_samples : pre.relay_vrf_modulo_samples,
|
relay_vrf_modulo_samples : pre.relay_vrf_modulo_samples,
|
||||||
|
ump_max_individual_weight : pre.ump_max_individual_weight,
|
||||||
pvf_checking_enabled : pre.pvf_checking_enabled,
|
pvf_checking_enabled : pre.pvf_checking_enabled,
|
||||||
pvf_voting_ttl : pre.pvf_voting_ttl,
|
pvf_voting_ttl : pre.pvf_voting_ttl,
|
||||||
minimum_validation_upgrade_delay : pre.minimum_validation_upgrade_delay,
|
minimum_validation_upgrade_delay : pre.minimum_validation_upgrade_delay,
|
||||||
|
|
||||||
ump_service_total_weight: Weight::from_ref_time(pre.ump_service_total_weight.0).set_proof_size(MAX_POV_SIZE as u64),
|
|
||||||
ump_max_individual_weight: Weight::from_ref_time(pre.ump_max_individual_weight.0).set_proof_size(MAX_POV_SIZE as u64),
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -221,7 +239,7 @@ ump_max_individual_weight: Weight::from_ref_time(pre.ump_max_individual_weight.0
|
|||||||
// to be unlikely to be caused by this. So we just log. Maybe it'll work out still?
|
// to be unlikely to be caused by this. So we just log. Maybe it'll work out still?
|
||||||
log::error!(
|
log::error!(
|
||||||
target: configuration::LOG_TARGET,
|
target: configuration::LOG_TARGET,
|
||||||
"unexpected error when performing translation of the configuration type during storage upgrade to v2."
|
"unexpected error when performing translation of the configuration type during storage upgrade to v4."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,31 +252,43 @@ mod tests {
|
|||||||
use crate::mock::{new_test_ext, Test};
|
use crate::mock::{new_test_ext, Test};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn v2_deserialized_from_actual_data() {
|
fn v3_deserialized_from_actual_data() {
|
||||||
// Fetched at Kusama 14,703,780 (0x3b2c305d01bd4adf1973d32a2d55ca1260a55eea8dfb3168e317c57f2841fdf1)
|
// Example how to get new `raw_config`:
|
||||||
|
// We'll obtain the raw_config hes for block
|
||||||
|
// 15,772,152 (0xf89d3ab5312c5f70d396dc59612f0aa65806c798346f9db4b35278baed2e0e53) on Kusama.
|
||||||
|
// Steps:
|
||||||
|
// 1. Go to Polkadot.js -> Developer -> Chain state -> Storage: https://polkadot.js.org/apps/#/chainstate
|
||||||
|
// 2. Set these parameters:
|
||||||
|
// 2.1. selected state query: configuration; activeConfig(): PolkadotRuntimeParachainsConfigurationHostConfiguration
|
||||||
|
// 2.2. blockhash to query at: 0xf89d3ab5312c5f70d396dc59612f0aa65806c798346f9db4b35278baed2e0e53 (the hash of the block)
|
||||||
|
// 2.3. Note the value of encoded storage key -> 0x06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385 for the referenced block.
|
||||||
|
// 2.4. You'll also need the decoded values to update the test.
|
||||||
|
// 3. Go to Polkadot.js -> Developer -> Chain state -> Raw storage
|
||||||
|
// 3.1 Enter the encoded storage key and you get the raw config.
|
||||||
|
|
||||||
|
// Fetched at Kusama 15,772,152 (0xf89d3ab5312c5f70d396dc59612f0aa65806c798346f9db4b35278baed2e0e53)
|
||||||
//
|
//
|
||||||
// This exceeds the maximal line width length, but that's fine, since this is not code and
|
// This exceeds the maximal line width length, but that's fine, since this is not code and
|
||||||
// doesn't need to be read and also leaving it as one line allows to easily copy it.
|
// doesn't need to be read and also leaving it as one line allows to easily copy it.
|
||||||
let raw_config = hex_literal::hex!["0000a000005000000a00000000c8000000c800000a0000000a000000100e0000580200000000500000c8000000e87648170000001e00000000000000005039278c0400000000000000000000005039278c0400000000000000000000e8030000009001001e00000000000000009001008070000000000000000000000a0000000a0000000a00000001000000010500000001c8000000060000005802000002000000580200000200000059000000000000001e0000002800000000c817a804000000000200000014000000"];
|
let raw_config = hex_literal::hex!["0000a000005000000a00000000c8000000c800000a0000000a000000100e0000580200000000500000c800000700e8764817020040011e00000000000000005039278c0400000000000000000000005039278c0400000000000000000000e8030000009001001e00000000000000009001008070000000000000000000000a0000000a0000000a00000001000000010500000001c8000000060000005802000002000000580200000200000059000000000000001e000000280000000700c817a80402004001000200000014000000"];
|
||||||
|
|
||||||
let v2 =
|
let v3 =
|
||||||
v3::OldHostConfiguration::<primitives::v2::BlockNumber>::decode(&mut &raw_config[..])
|
v4::OldHostConfiguration::<primitives::v2::BlockNumber>::decode(&mut &raw_config[..])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// We check only a sample of the values here. If we missed any fields or messed up data types
|
// We check only a sample of the values here. If we missed any fields or messed up data types
|
||||||
// that would skew all the fields coming after.
|
// that would skew all the fields coming after.
|
||||||
assert_eq!(v2.max_code_size, 10_485_760);
|
assert_eq!(v3.max_code_size, 10_485_760);
|
||||||
assert_eq!(v2.validation_upgrade_cooldown, 3600);
|
assert_eq!(v3.validation_upgrade_cooldown, 3600);
|
||||||
assert_eq!(v2.max_pov_size, 5_242_880);
|
assert_eq!(v3.max_pov_size, 5_242_880);
|
||||||
assert_eq!(v2.hrmp_channel_max_message_size, 102_400);
|
assert_eq!(v3.hrmp_channel_max_message_size, 102_400);
|
||||||
assert_eq!(v2.dispute_max_spam_slots, 2);
|
assert_eq!(v3.n_delay_tranches, 89);
|
||||||
assert_eq!(v2.n_delay_tranches, 89);
|
assert_eq!(v3.ump_max_individual_weight, Weight::from_parts(20_000_000_000, 5_242_880));
|
||||||
assert_eq!(v2.ump_max_individual_weight, OldWeight(20_000_000_000));
|
assert_eq!(v3.minimum_validation_upgrade_delay, 20);
|
||||||
assert_eq!(v2.minimum_validation_upgrade_delay, 20);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_migrate_to_v3() {
|
fn test_migrate_to_v4() {
|
||||||
// Host configuration has lots of fields. However, in this migration we add only a couple of
|
// Host configuration has lots of fields. However, in this migration we add only a couple of
|
||||||
// fields. The most important part to check are a couple of the last fields. We also pick
|
// fields. The most important part to check are a couple of the last fields. We also pick
|
||||||
// extra fields to check arbitrarily, e.g. depending on their position (i.e. the middle) and
|
// extra fields to check arbitrarily, e.g. depending on their position (i.e. the middle) and
|
||||||
@@ -267,8 +297,8 @@ mod tests {
|
|||||||
// We specify only the picked fields and the rest should be provided by the `Default`
|
// We specify only the picked fields and the rest should be provided by the `Default`
|
||||||
// implementation. That implementation is copied over between the two types and should work
|
// implementation. That implementation is copied over between the two types and should work
|
||||||
// fine.
|
// fine.
|
||||||
let v2 = v3::OldHostConfiguration::<primitives::v2::BlockNumber> {
|
let v3 = v4::OldHostConfiguration::<primitives::v2::BlockNumber> {
|
||||||
ump_max_individual_weight: OldWeight(0x71616e6f6e0au64),
|
ump_max_individual_weight: Weight::from_parts(0x71616e6f6e0au64, 0x71616e6f6e0au64),
|
||||||
needed_approvals: 69,
|
needed_approvals: 69,
|
||||||
thread_availability_period: 55,
|
thread_availability_period: 55,
|
||||||
hrmp_recipient_deposit: 1337,
|
hrmp_recipient_deposit: 1337,
|
||||||
@@ -279,64 +309,61 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
new_test_ext(Default::default()).execute_with(|| {
|
new_test_ext(Default::default()).execute_with(|| {
|
||||||
// Implant the v2 version in the state.
|
// Implant the v3 version in the state.
|
||||||
frame_support::storage::unhashed::put_raw(
|
frame_support::storage::unhashed::put_raw(
|
||||||
&configuration::ActiveConfig::<Test>::hashed_key(),
|
&configuration::ActiveConfig::<Test>::hashed_key(),
|
||||||
&v2.encode(),
|
&v3.encode(),
|
||||||
);
|
);
|
||||||
|
|
||||||
migrate_to_v3::<Test>();
|
migrate_to_v4::<Test>();
|
||||||
|
|
||||||
let v3 = configuration::ActiveConfig::<Test>::get();
|
let v4 = configuration::ActiveConfig::<Test>::get();
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
{
|
{
|
||||||
assert_eq!(v2.max_code_size , v3.max_code_size);
|
assert_eq!(v3.max_code_size , v4.max_code_size);
|
||||||
assert_eq!(v2.max_head_data_size , v3.max_head_data_size);
|
assert_eq!(v3.max_head_data_size , v4.max_head_data_size);
|
||||||
assert_eq!(v2.max_upward_queue_count , v3.max_upward_queue_count);
|
assert_eq!(v3.max_upward_queue_count , v4.max_upward_queue_count);
|
||||||
assert_eq!(v2.max_upward_queue_size , v3.max_upward_queue_size);
|
assert_eq!(v3.max_upward_queue_size , v4.max_upward_queue_size);
|
||||||
assert_eq!(v2.max_upward_message_size , v3.max_upward_message_size);
|
assert_eq!(v3.max_upward_message_size , v4.max_upward_message_size);
|
||||||
assert_eq!(v2.max_upward_message_num_per_candidate , v3.max_upward_message_num_per_candidate);
|
assert_eq!(v3.max_upward_message_num_per_candidate , v4.max_upward_message_num_per_candidate);
|
||||||
assert_eq!(v2.hrmp_max_message_num_per_candidate , v3.hrmp_max_message_num_per_candidate);
|
assert_eq!(v3.hrmp_max_message_num_per_candidate , v4.hrmp_max_message_num_per_candidate);
|
||||||
assert_eq!(v2.validation_upgrade_cooldown , v3.validation_upgrade_cooldown);
|
assert_eq!(v3.validation_upgrade_cooldown , v4.validation_upgrade_cooldown);
|
||||||
assert_eq!(v2.validation_upgrade_delay , v3.validation_upgrade_delay);
|
assert_eq!(v3.validation_upgrade_delay , v4.validation_upgrade_delay);
|
||||||
assert_eq!(v2.max_pov_size , v3.max_pov_size);
|
assert_eq!(v3.max_pov_size , v4.max_pov_size);
|
||||||
assert_eq!(v2.max_downward_message_size , v3.max_downward_message_size);
|
assert_eq!(v3.max_downward_message_size , v4.max_downward_message_size);
|
||||||
assert_eq!(v2.hrmp_max_parachain_outbound_channels , v3.hrmp_max_parachain_outbound_channels);
|
assert_eq!(v3.ump_service_total_weight , v4.ump_service_total_weight);
|
||||||
assert_eq!(v2.hrmp_max_parathread_outbound_channels , v3.hrmp_max_parathread_outbound_channels);
|
assert_eq!(v3.hrmp_max_parachain_outbound_channels , v4.hrmp_max_parachain_outbound_channels);
|
||||||
assert_eq!(v2.hrmp_sender_deposit , v3.hrmp_sender_deposit);
|
assert_eq!(v3.hrmp_max_parathread_outbound_channels , v4.hrmp_max_parathread_outbound_channels);
|
||||||
assert_eq!(v2.hrmp_recipient_deposit , v3.hrmp_recipient_deposit);
|
assert_eq!(v3.hrmp_sender_deposit , v4.hrmp_sender_deposit);
|
||||||
assert_eq!(v2.hrmp_channel_max_capacity , v3.hrmp_channel_max_capacity);
|
assert_eq!(v3.hrmp_recipient_deposit , v4.hrmp_recipient_deposit);
|
||||||
assert_eq!(v2.hrmp_channel_max_total_size , v3.hrmp_channel_max_total_size);
|
assert_eq!(v3.hrmp_channel_max_capacity , v4.hrmp_channel_max_capacity);
|
||||||
assert_eq!(v2.hrmp_max_parachain_inbound_channels , v3.hrmp_max_parachain_inbound_channels);
|
assert_eq!(v3.hrmp_channel_max_total_size , v4.hrmp_channel_max_total_size);
|
||||||
assert_eq!(v2.hrmp_max_parathread_inbound_channels , v3.hrmp_max_parathread_inbound_channels);
|
assert_eq!(v3.hrmp_max_parachain_inbound_channels , v4.hrmp_max_parachain_inbound_channels);
|
||||||
assert_eq!(v2.hrmp_channel_max_message_size , v3.hrmp_channel_max_message_size);
|
assert_eq!(v3.hrmp_max_parathread_inbound_channels , v4.hrmp_max_parathread_inbound_channels);
|
||||||
assert_eq!(v2.code_retention_period , v3.code_retention_period);
|
assert_eq!(v3.hrmp_channel_max_message_size , v4.hrmp_channel_max_message_size);
|
||||||
assert_eq!(v2.parathread_cores , v3.parathread_cores);
|
assert_eq!(v3.code_retention_period , v4.code_retention_period);
|
||||||
assert_eq!(v2.parathread_retries , v3.parathread_retries);
|
assert_eq!(v3.parathread_cores , v4.parathread_cores);
|
||||||
assert_eq!(v2.group_rotation_frequency , v3.group_rotation_frequency);
|
assert_eq!(v3.parathread_retries , v4.parathread_retries);
|
||||||
assert_eq!(v2.chain_availability_period , v3.chain_availability_period);
|
assert_eq!(v3.group_rotation_frequency , v4.group_rotation_frequency);
|
||||||
assert_eq!(v2.thread_availability_period , v3.thread_availability_period);
|
assert_eq!(v3.chain_availability_period , v4.chain_availability_period);
|
||||||
assert_eq!(v2.scheduling_lookahead , v3.scheduling_lookahead);
|
assert_eq!(v3.thread_availability_period , v4.thread_availability_period);
|
||||||
assert_eq!(v2.max_validators_per_core , v3.max_validators_per_core);
|
assert_eq!(v3.scheduling_lookahead , v4.scheduling_lookahead);
|
||||||
assert_eq!(v2.max_validators , v3.max_validators);
|
assert_eq!(v3.max_validators_per_core , v4.max_validators_per_core);
|
||||||
assert_eq!(v2.dispute_period , v3.dispute_period);
|
assert_eq!(v3.max_validators , v4.max_validators);
|
||||||
assert_eq!(v2.dispute_post_conclusion_acceptance_period, v3.dispute_post_conclusion_acceptance_period);
|
assert_eq!(v3.dispute_period , v4.dispute_period);
|
||||||
assert_eq!(v2.dispute_max_spam_slots , v3.dispute_max_spam_slots);
|
assert_eq!(v3.dispute_post_conclusion_acceptance_period, v4.dispute_post_conclusion_acceptance_period);
|
||||||
assert_eq!(v2.dispute_conclusion_by_time_out_period , v3.dispute_conclusion_by_time_out_period);
|
assert_eq!(v3.dispute_conclusion_by_time_out_period , v4.dispute_conclusion_by_time_out_period);
|
||||||
assert_eq!(v2.no_show_slots , v3.no_show_slots);
|
assert_eq!(v3.no_show_slots , v4.no_show_slots);
|
||||||
assert_eq!(v2.n_delay_tranches , v3.n_delay_tranches);
|
assert_eq!(v3.n_delay_tranches , v4.n_delay_tranches);
|
||||||
assert_eq!(v2.zeroth_delay_tranche_width , v3.zeroth_delay_tranche_width);
|
assert_eq!(v3.zeroth_delay_tranche_width , v4.zeroth_delay_tranche_width);
|
||||||
assert_eq!(v2.needed_approvals , v3.needed_approvals);
|
assert_eq!(v3.needed_approvals , v4.needed_approvals);
|
||||||
assert_eq!(v2.relay_vrf_modulo_samples , v3.relay_vrf_modulo_samples);
|
assert_eq!(v3.relay_vrf_modulo_samples , v4.relay_vrf_modulo_samples);
|
||||||
assert_eq!(v2.pvf_checking_enabled , v3.pvf_checking_enabled);
|
assert_eq!(v3.ump_max_individual_weight , v4.ump_max_individual_weight);
|
||||||
assert_eq!(v2.pvf_voting_ttl , v3.pvf_voting_ttl);
|
assert_eq!(v3.pvf_checking_enabled , v4.pvf_checking_enabled);
|
||||||
assert_eq!(v2.minimum_validation_upgrade_delay , v3.minimum_validation_upgrade_delay);
|
assert_eq!(v3.pvf_voting_ttl , v4.pvf_voting_ttl);
|
||||||
|
assert_eq!(v3.minimum_validation_upgrade_delay , v4.minimum_validation_upgrade_delay);
|
||||||
|
|
||||||
assert_eq!(v2.ump_service_total_weight, OldWeight(v3.ump_service_total_weight.ref_time()));
|
|
||||||
assert_eq!(v2.ump_max_individual_weight, OldWeight(v3.ump_max_individual_weight.ref_time()));
|
|
||||||
assert_eq!(v3.ump_service_total_weight.proof_size(), MAX_POV_SIZE as u64);
|
|
||||||
assert_eq!(v3.ump_max_individual_weight.proof_size(), MAX_POV_SIZE as u64);
|
|
||||||
}; // ; makes this a statement. `rustfmt::skip` cannot be put on an expression.
|
}; // ; makes this a statement. `rustfmt::skip` cannot be put on an expression.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,7 +309,6 @@ fn setting_pending_config_members() {
|
|||||||
max_validators: None,
|
max_validators: None,
|
||||||
dispute_period: 239,
|
dispute_period: 239,
|
||||||
dispute_post_conclusion_acceptance_period: 10,
|
dispute_post_conclusion_acceptance_period: 10,
|
||||||
dispute_max_spam_slots: 2,
|
|
||||||
dispute_conclusion_by_time_out_period: 512,
|
dispute_conclusion_by_time_out_period: 512,
|
||||||
no_show_slots: 240,
|
no_show_slots: 240,
|
||||||
n_delay_tranches: 241,
|
n_delay_tranches: 241,
|
||||||
@@ -402,11 +401,6 @@ fn setting_pending_config_members() {
|
|||||||
new_config.dispute_post_conclusion_acceptance_period,
|
new_config.dispute_post_conclusion_acceptance_period,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Configuration::set_dispute_max_spam_slots(
|
|
||||||
RuntimeOrigin::root(),
|
|
||||||
new_config.dispute_max_spam_slots,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
Configuration::set_dispute_conclusion_by_time_out_period(
|
Configuration::set_dispute_conclusion_by_time_out_period(
|
||||||
RuntimeOrigin::root(),
|
RuntimeOrigin::root(),
|
||||||
new_config.dispute_conclusion_by_time_out_period,
|
new_config.dispute_conclusion_by_time_out_period,
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ mod tests;
|
|||||||
#[cfg(feature = "runtime-benchmarks")]
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
mod benchmarking;
|
mod benchmarking;
|
||||||
|
|
||||||
|
pub mod migration;
|
||||||
|
|
||||||
|
const LOG_TARGET: &str = "runtime::disputes";
|
||||||
|
|
||||||
/// Whether the dispute is local or remote.
|
/// Whether the dispute is local or remote.
|
||||||
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
||||||
pub enum DisputeLocation {
|
pub enum DisputeLocation {
|
||||||
@@ -262,7 +266,6 @@ pub trait DisputesHandler<BlockNumber: Ord> {
|
|||||||
/// accounting for maximum block weight.
|
/// accounting for maximum block weight.
|
||||||
fn filter_dispute_data(
|
fn filter_dispute_data(
|
||||||
statement_set: DisputeStatementSet,
|
statement_set: DisputeStatementSet,
|
||||||
max_spam_slots: u32,
|
|
||||||
post_conclusion_acceptance_period: BlockNumber,
|
post_conclusion_acceptance_period: BlockNumber,
|
||||||
verify_sigs: VerifyDisputeSignatures,
|
verify_sigs: VerifyDisputeSignatures,
|
||||||
) -> Option<CheckedDisputeStatementSet>;
|
) -> Option<CheckedDisputeStatementSet>;
|
||||||
@@ -311,7 +314,6 @@ impl<BlockNumber: Ord> DisputesHandler<BlockNumber> for () {
|
|||||||
|
|
||||||
fn filter_dispute_data(
|
fn filter_dispute_data(
|
||||||
_set: DisputeStatementSet,
|
_set: DisputeStatementSet,
|
||||||
_max_spam_slots: u32,
|
|
||||||
_post_conclusion_acceptance_period: BlockNumber,
|
_post_conclusion_acceptance_period: BlockNumber,
|
||||||
_verify_sigs: VerifyDisputeSignatures,
|
_verify_sigs: VerifyDisputeSignatures,
|
||||||
) -> Option<CheckedDisputeStatementSet> {
|
) -> Option<CheckedDisputeStatementSet> {
|
||||||
@@ -361,14 +363,12 @@ where
|
|||||||
|
|
||||||
fn filter_dispute_data(
|
fn filter_dispute_data(
|
||||||
set: DisputeStatementSet,
|
set: DisputeStatementSet,
|
||||||
max_spam_slots: u32,
|
|
||||||
post_conclusion_acceptance_period: T::BlockNumber,
|
post_conclusion_acceptance_period: T::BlockNumber,
|
||||||
verify_sigs: VerifyDisputeSignatures,
|
verify_sigs: VerifyDisputeSignatures,
|
||||||
) -> Option<CheckedDisputeStatementSet> {
|
) -> Option<CheckedDisputeStatementSet> {
|
||||||
pallet::Pallet::<T>::filter_dispute_data(
|
pallet::Pallet::<T>::filter_dispute_data(
|
||||||
&set,
|
&set,
|
||||||
post_conclusion_acceptance_period,
|
post_conclusion_acceptance_period,
|
||||||
max_spam_slots,
|
|
||||||
verify_sigs,
|
verify_sigs,
|
||||||
)
|
)
|
||||||
.filter_statement_set(set)
|
.filter_statement_set(set)
|
||||||
@@ -471,14 +471,6 @@ pub mod pallet {
|
|||||||
T::BlockNumber,
|
T::BlockNumber,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/// Maps session indices to a vector indicating the number of potentially-spam disputes
|
|
||||||
/// each validator is participating in. Potentially-spam disputes are remote disputes which have
|
|
||||||
/// fewer than `byzantine_threshold + 1` validators.
|
|
||||||
///
|
|
||||||
/// The i'th entry of the vector corresponds to the i'th validator in the session.
|
|
||||||
#[pallet::storage]
|
|
||||||
pub(super) type SpamSlots<T> = StorageMap<_, Twox64Concat, SessionIndex, Vec<u32>>;
|
|
||||||
|
|
||||||
/// Whether the chain is frozen. Starts as `None`. When this is `Some`,
|
/// Whether the chain is frozen. Starts as `None`. When this is `Some`,
|
||||||
/// the chain will not accept any new parachain blocks for backing or inclusion,
|
/// the chain will not accept any new parachain blocks for backing or inclusion,
|
||||||
/// and its value indicates the last valid block number in the chain.
|
/// and its value indicates the last valid block number in the chain.
|
||||||
@@ -517,10 +509,10 @@ pub mod pallet {
|
|||||||
InvalidSignature,
|
InvalidSignature,
|
||||||
/// Validator vote submitted more than once to dispute.
|
/// Validator vote submitted more than once to dispute.
|
||||||
DuplicateStatement,
|
DuplicateStatement,
|
||||||
/// Too many spam slots used by some specific validator.
|
|
||||||
PotentialSpam,
|
|
||||||
/// A dispute where there are only votes on one side.
|
/// A dispute where there are only votes on one side.
|
||||||
SingleSidedDispute,
|
SingleSidedDispute,
|
||||||
|
/// Unconfirmed dispute statement sets provided
|
||||||
|
UnconfirmedDispute,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::call]
|
#[pallet::call]
|
||||||
@@ -574,19 +566,9 @@ impl DisputeStateFlags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, RuntimeDebug)]
|
|
||||||
enum SpamSlotChange {
|
|
||||||
/// Add a `+1` to the spam slot for a particular validator index in this session.
|
|
||||||
Inc,
|
|
||||||
/// Subtract `-1` ...
|
|
||||||
Dec,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ImportSummary<BlockNumber> {
|
struct ImportSummary<BlockNumber> {
|
||||||
/// The new state, with all votes imported.
|
/// The new state, with all votes imported.
|
||||||
state: DisputeState<BlockNumber>,
|
state: DisputeState<BlockNumber>,
|
||||||
/// Changes to spam slots. Validator index paired with directional change.
|
|
||||||
spam_slot_changes: Vec<(ValidatorIndex, SpamSlotChange)>,
|
|
||||||
/// Validators to slash for being (wrongly) on the AGAINST side.
|
/// Validators to slash for being (wrongly) on the AGAINST side.
|
||||||
slash_against: Vec<ValidatorIndex>,
|
slash_against: Vec<ValidatorIndex>,
|
||||||
/// Validators to slash for being (wrongly) on the FOR side.
|
/// Validators to slash for being (wrongly) on the FOR side.
|
||||||
@@ -699,37 +681,7 @@ impl<BlockNumber: Clone> DisputeStateImporter<BlockNumber> {
|
|||||||
|
|
||||||
let pre_post_contains = |flags| (pre_flags.contains(flags), post_flags.contains(flags));
|
let pre_post_contains = |flags| (pre_flags.contains(flags), post_flags.contains(flags));
|
||||||
|
|
||||||
// 1. Act on confirmed flag state to inform spam slots changes.
|
// 1. Check for fresh FOR supermajority. Only if not already concluded.
|
||||||
let spam_slot_changes: Vec<_> = match pre_post_contains(DisputeStateFlags::CONFIRMED) {
|
|
||||||
(false, false) => {
|
|
||||||
// increment spam slots for all new participants.
|
|
||||||
self.new_participants
|
|
||||||
.iter_ones()
|
|
||||||
.map(|i| (ValidatorIndex(i as _), SpamSlotChange::Inc))
|
|
||||||
.collect()
|
|
||||||
},
|
|
||||||
(false, true) => {
|
|
||||||
// all participants, which are not new participants
|
|
||||||
let prev_participants = (self.state.validators_for.clone() |
|
|
||||||
self.state.validators_against.clone()) &
|
|
||||||
!self.new_participants.clone();
|
|
||||||
|
|
||||||
prev_participants
|
|
||||||
.iter_ones()
|
|
||||||
.map(|i| (ValidatorIndex(i as _), SpamSlotChange::Dec))
|
|
||||||
.collect()
|
|
||||||
},
|
|
||||||
(true, false) => {
|
|
||||||
log::error!("Dispute statements are never removed. This is a bug");
|
|
||||||
Vec::new()
|
|
||||||
},
|
|
||||||
(true, true) => {
|
|
||||||
// No change, nothing to do.
|
|
||||||
Vec::new()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// 2. Check for fresh FOR supermajority. Only if not already concluded.
|
|
||||||
let slash_against =
|
let slash_against =
|
||||||
if let (false, true) = pre_post_contains(DisputeStateFlags::FOR_SUPERMAJORITY) {
|
if let (false, true) = pre_post_contains(DisputeStateFlags::FOR_SUPERMAJORITY) {
|
||||||
if self.state.concluded_at.is_none() {
|
if self.state.concluded_at.is_none() {
|
||||||
@@ -746,7 +698,7 @@ impl<BlockNumber: Clone> DisputeStateImporter<BlockNumber> {
|
|||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
// 3. Check for fresh AGAINST supermajority.
|
// 2. Check for fresh AGAINST supermajority.
|
||||||
let slash_for =
|
let slash_for =
|
||||||
if let (false, true) = pre_post_contains(DisputeStateFlags::AGAINST_SUPERMAJORITY) {
|
if let (false, true) = pre_post_contains(DisputeStateFlags::AGAINST_SUPERMAJORITY) {
|
||||||
if self.state.concluded_at.is_none() {
|
if self.state.concluded_at.is_none() {
|
||||||
@@ -761,7 +713,6 @@ impl<BlockNumber: Clone> DisputeStateImporter<BlockNumber> {
|
|||||||
|
|
||||||
ImportSummary {
|
ImportSummary {
|
||||||
state: self.state,
|
state: self.state,
|
||||||
spam_slot_changes,
|
|
||||||
slash_against,
|
slash_against,
|
||||||
slash_for,
|
slash_for,
|
||||||
new_participants: self.new_participants,
|
new_participants: self.new_participants,
|
||||||
@@ -831,32 +782,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
dispute.concluded_at = Some(now);
|
dispute.concluded_at = Some(now);
|
||||||
<Disputes<T>>::insert(session_index, candidate_hash, &dispute);
|
<Disputes<T>>::insert(session_index, candidate_hash, &dispute);
|
||||||
|
|
||||||
if <Included<T>>::contains_key(&session_index, &candidate_hash) {
|
weight += T::DbWeight::get().writes(1);
|
||||||
// Local disputes don't count towards spam.
|
|
||||||
|
|
||||||
weight += T::DbWeight::get().reads_writes(1, 1);
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// mildly punish all validators involved. they've failed to make
|
|
||||||
// data available to others, so this is most likely spam.
|
|
||||||
SpamSlots::<T>::mutate(session_index, |spam_slots| {
|
|
||||||
let spam_slots = match spam_slots {
|
|
||||||
Some(ref mut s) => s,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
// also reduce spam slots for all validators involved, if the dispute was unconfirmed.
|
|
||||||
// this does open us up to more spam, but only for validators who are willing
|
|
||||||
// to be punished more.
|
|
||||||
//
|
|
||||||
// it would be unexpected for any change here to occur when the dispute has not concluded
|
|
||||||
// in time, as a dispute guaranteed to have at least one honest participant should
|
|
||||||
// conclude quickly.
|
|
||||||
let _participating = decrement_spam(spam_slots, &dispute);
|
|
||||||
});
|
|
||||||
|
|
||||||
weight += T::DbWeight::get().reads_writes(2, 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -894,7 +820,6 @@ impl<T: Config> Pallet<T> {
|
|||||||
// TODO: https://github.com/paritytech/polkadot/issues/3469
|
// TODO: https://github.com/paritytech/polkadot/issues/3469
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
<Included<T>>::remove_prefix(to_prune, None);
|
<Included<T>>::remove_prefix(to_prune, None);
|
||||||
SpamSlots::<T>::remove(to_prune);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*last_pruned = Some(pruning_target);
|
*last_pruned = Some(pruning_target);
|
||||||
@@ -938,10 +863,10 @@ impl<T: Config> Pallet<T> {
|
|||||||
//
|
//
|
||||||
// Votes which are duplicate or already known by the chain are filtered out.
|
// Votes which are duplicate or already known by the chain are filtered out.
|
||||||
// The entire set is removed if the dispute is both, ancient and concluded.
|
// The entire set is removed if the dispute is both, ancient and concluded.
|
||||||
|
// Disputes without enough votes to get confirmed are also filtered out.
|
||||||
fn filter_dispute_data(
|
fn filter_dispute_data(
|
||||||
set: &DisputeStatementSet,
|
set: &DisputeStatementSet,
|
||||||
post_conclusion_acceptance_period: <T as frame_system::Config>::BlockNumber,
|
post_conclusion_acceptance_period: <T as frame_system::Config>::BlockNumber,
|
||||||
max_spam_slots: u32,
|
|
||||||
verify_sigs: VerifyDisputeSignatures,
|
verify_sigs: VerifyDisputeSignatures,
|
||||||
) -> StatementSetFilter {
|
) -> StatementSetFilter {
|
||||||
let mut filter = StatementSetFilter::RemoveIndices(Vec::new());
|
let mut filter = StatementSetFilter::RemoveIndices(Vec::new());
|
||||||
@@ -960,29 +885,26 @@ impl<T: Config> Pallet<T> {
|
|||||||
let n_validators = session_info.validators.len();
|
let n_validators = session_info.validators.len();
|
||||||
|
|
||||||
// Check for ancient.
|
// Check for ancient.
|
||||||
let (first_votes, dispute_state) = {
|
let dispute_state = {
|
||||||
if let Some(dispute_state) = <Disputes<T>>::get(&set.session, &set.candidate_hash) {
|
if let Some(dispute_state) = <Disputes<T>>::get(&set.session, &set.candidate_hash) {
|
||||||
if dispute_state.concluded_at.as_ref().map_or(false, |c| c < &oldest_accepted) {
|
if dispute_state.concluded_at.as_ref().map_or(false, |c| c < &oldest_accepted) {
|
||||||
return StatementSetFilter::RemoveAll
|
return StatementSetFilter::RemoveAll
|
||||||
}
|
}
|
||||||
|
|
||||||
(false, dispute_state)
|
dispute_state
|
||||||
} else {
|
} else {
|
||||||
// No state in storage, this indicates it's the first dispute statement set as well.
|
// No state in storage, this indicates it's the first dispute statement set as well.
|
||||||
(
|
DisputeState {
|
||||||
true,
|
validators_for: bitvec![u8, BitOrderLsb0; 0; n_validators],
|
||||||
DisputeState {
|
validators_against: bitvec![u8, BitOrderLsb0; 0; n_validators],
|
||||||
validators_for: bitvec![u8, BitOrderLsb0; 0; n_validators],
|
start: now,
|
||||||
validators_against: bitvec![u8, BitOrderLsb0; 0; n_validators],
|
concluded_at: None,
|
||||||
start: now,
|
}
|
||||||
concluded_at: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check and import all votes.
|
// Check and import all votes.
|
||||||
let mut summary = {
|
let summary = {
|
||||||
let mut importer = DisputeStateImporter::new(dispute_state, now);
|
let mut importer = DisputeStateImporter::new(dispute_state, now);
|
||||||
for (i, (statement, validator_index, signature)) in set.statements.iter().enumerate() {
|
for (i, (statement, validator_index, signature)) in set.statements.iter().enumerate() {
|
||||||
// assure the validator index and is present in the session info
|
// assure the validator index and is present in the session info
|
||||||
@@ -1039,99 +961,11 @@ impl<T: Config> Pallet<T> {
|
|||||||
return StatementSetFilter::RemoveAll
|
return StatementSetFilter::RemoveAll
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply spam slot changes. Bail early if too many occupied.
|
// Reject disputes containing less votes than needed for confirmation.
|
||||||
let is_local = <Included<T>>::contains_key(&set.session, &set.candidate_hash);
|
if (summary.state.validators_for.clone() | &summary.state.validators_against).count_ones() <=
|
||||||
if !is_local {
|
byzantine_threshold(summary.state.validators_for.len())
|
||||||
let mut spam_slots: Vec<u32> =
|
{
|
||||||
SpamSlots::<T>::get(&set.session).unwrap_or_else(|| vec![0; n_validators]);
|
return StatementSetFilter::RemoveAll
|
||||||
let mut spam_filter_struck = false;
|
|
||||||
for (validator_index, spam_slot_change) in summary.spam_slot_changes {
|
|
||||||
let spam_slot = spam_slots
|
|
||||||
.get_mut(validator_index.0 as usize)
|
|
||||||
.expect("index is in-bounds, as checked above; qed");
|
|
||||||
|
|
||||||
if let SpamSlotChange::Inc = spam_slot_change {
|
|
||||||
if *spam_slot >= max_spam_slots {
|
|
||||||
spam_filter_struck = true;
|
|
||||||
|
|
||||||
// Find the vote by this validator and filter it out.
|
|
||||||
let first_index_in_set = set
|
|
||||||
.statements
|
|
||||||
.iter()
|
|
||||||
.position(|(_statement, v_i, _signature)| &validator_index == v_i)
|
|
||||||
.expect(
|
|
||||||
"spam slots are only incremented when a new statement \
|
|
||||||
from a validator is included; qed",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Note that there may be many votes by the validator in the statement
|
|
||||||
// set. There are not supposed to be, but the purpose of this function
|
|
||||||
// is to filter out invalid submissions, after all.
|
|
||||||
//
|
|
||||||
// This is fine - we only need to handle the first one, because all
|
|
||||||
// subsequent votes' indices have been added to the filter already
|
|
||||||
// by the duplicate checks above. It's only the first one which
|
|
||||||
// may not already have been filtered out.
|
|
||||||
filter.remove_index(first_index_in_set);
|
|
||||||
|
|
||||||
// Removing individual statments can cause the dispute to become onesided.
|
|
||||||
// Checking that (again) is done after the loop. Remove the bit indices.
|
|
||||||
summary.new_participants.set(validator_index.0 as _, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's also worth noting that the `DisputeStateImporter`
|
|
||||||
// which produces these spam slot updates only produces
|
|
||||||
// one spam slot update per validator because it rejects
|
|
||||||
// duplicate votes.
|
|
||||||
//
|
|
||||||
// So we don't need to worry about spam slots being
|
|
||||||
// updated incorrectly after receiving duplicates.
|
|
||||||
*spam_slot += 1;
|
|
||||||
} else {
|
|
||||||
*spam_slot = spam_slot.saturating_sub(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We write the spam slots here because sequential calls to
|
|
||||||
// `filter_dispute_data` have a dependency on each other.
|
|
||||||
//
|
|
||||||
// For example, if a validator V occupies 1 spam slot and
|
|
||||||
// max is 2, then 2 sequential calls incrementing spam slot
|
|
||||||
// cannot be allowed.
|
|
||||||
//
|
|
||||||
// However, 3 sequential calls, where the first increments,
|
|
||||||
// the second decrements, and the third increments would be allowed.
|
|
||||||
SpamSlots::<T>::insert(&set.session, spam_slots);
|
|
||||||
|
|
||||||
// This is only relevant in cases where it's the first vote and the state
|
|
||||||
// would hence hold a onesided dispute. If a onesided dispute can never be
|
|
||||||
// started, by induction, we can never enter a state of a one sided dispute.
|
|
||||||
if spam_filter_struck && first_votes {
|
|
||||||
let mut vote_for_count = 0_u64;
|
|
||||||
let mut vote_against_count = 0_u64;
|
|
||||||
// Since this is the first set of statements for the dispute,
|
|
||||||
// it's sufficient to count the votes in the statement set after they
|
|
||||||
set.statements.iter().for_each(|(statement, v_i, _signature)| {
|
|
||||||
if Some(true) ==
|
|
||||||
summary.new_participants.get(v_i.0 as usize).map(|b| *b.as_ref())
|
|
||||||
{
|
|
||||||
match statement {
|
|
||||||
// `summary.new_flags` contains the spam free votes.
|
|
||||||
// Note that this does not distinguish between pro or con votes,
|
|
||||||
// since allowing both of them, even if the spam threshold would be reached
|
|
||||||
// is a good thing.
|
|
||||||
// Overflow of the counters is no concern, disputes are limited by weight.
|
|
||||||
DisputeStatement::Valid(_) => vote_for_count += 1,
|
|
||||||
DisputeStatement::Invalid(_) => vote_against_count += 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if vote_for_count.is_zero() || vote_against_count.is_zero() {
|
|
||||||
// It wasn't one-sided before the spam filters, but now it is,
|
|
||||||
// so we need to be thorough and not import that dispute.
|
|
||||||
return StatementSetFilter::RemoveAll
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filter
|
filter
|
||||||
@@ -1201,13 +1035,17 @@ impl<T: Config> Pallet<T> {
|
|||||||
Error::<T>::SingleSidedDispute,
|
Error::<T>::SingleSidedDispute,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Reject disputes containing less votes than needed for confirmation.
|
||||||
|
ensure!(
|
||||||
|
(summary.state.validators_for.clone() | &summary.state.validators_against).count_ones() >
|
||||||
|
byzantine_threshold(summary.state.validators_for.len()),
|
||||||
|
Error::<T>::UnconfirmedDispute,
|
||||||
|
);
|
||||||
|
|
||||||
let DisputeStatementSet { ref session, ref candidate_hash, .. } = set;
|
let DisputeStatementSet { ref session, ref candidate_hash, .. } = set;
|
||||||
let session = *session;
|
let session = *session;
|
||||||
let candidate_hash = *candidate_hash;
|
let candidate_hash = *candidate_hash;
|
||||||
|
|
||||||
// we can omit spam slot checks, `fn filter_disputes_data` is
|
|
||||||
// always called before calling this `fn`.
|
|
||||||
|
|
||||||
if fresh {
|
if fresh {
|
||||||
let is_local = <Included<T>>::contains_key(&session, &candidate_hash);
|
let is_local = <Included<T>>::contains_key(&session, &candidate_hash);
|
||||||
|
|
||||||
@@ -1283,15 +1121,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
|
|
||||||
<Included<T>>::insert(&session, &candidate_hash, revert_to);
|
<Included<T>>::insert(&session, &candidate_hash, revert_to);
|
||||||
|
|
||||||
// If we just included a block locally which has a live dispute, decrement spam slots
|
|
||||||
// for any involved validators, if the dispute is not already confirmed by f + 1.
|
|
||||||
if let Some(state) = <Disputes<T>>::get(&session, candidate_hash) {
|
if let Some(state) = <Disputes<T>>::get(&session, candidate_hash) {
|
||||||
SpamSlots::<T>::mutate(&session, |spam_slots| {
|
|
||||||
if let Some(ref mut spam_slots) = *spam_slots {
|
|
||||||
decrement_spam(spam_slots, &state);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if has_supermajority_against(&state) {
|
if has_supermajority_against(&state) {
|
||||||
Self::revert_and_freeze(revert_to);
|
Self::revert_and_freeze(revert_to);
|
||||||
}
|
}
|
||||||
@@ -1337,29 +1167,6 @@ fn has_supermajority_against<BlockNumber>(dispute: &DisputeState<BlockNumber>) -
|
|||||||
dispute.validators_against.count_ones() >= supermajority_threshold
|
dispute.validators_against.count_ones() >= supermajority_threshold
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the dispute had not enough validators to confirm, decrement spam slots for all the participating
|
|
||||||
// validators.
|
|
||||||
//
|
|
||||||
// Returns the set of participating validators as a bitvec.
|
|
||||||
fn decrement_spam<BlockNumber>(
|
|
||||||
spam_slots: &mut [u32],
|
|
||||||
dispute: &DisputeState<BlockNumber>,
|
|
||||||
) -> bitvec::vec::BitVec<u8, BitOrderLsb0> {
|
|
||||||
let byzantine_threshold = byzantine_threshold(spam_slots.len());
|
|
||||||
|
|
||||||
let participating = dispute.validators_for.clone() | dispute.validators_against.clone();
|
|
||||||
let decrement_spam = participating.count_ones() <= byzantine_threshold;
|
|
||||||
for validator_index in participating.iter_ones() {
|
|
||||||
if decrement_spam {
|
|
||||||
if let Some(occupied) = spam_slots.get_mut(validator_index as usize) {
|
|
||||||
*occupied = occupied.saturating_sub(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
participating
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_signature(
|
fn check_signature(
|
||||||
validator_public: &ValidatorId,
|
validator_public: &ValidatorId,
|
||||||
candidate_hash: CandidateHash,
|
candidate_hash: CandidateHash,
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2017-2022 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Polkadot is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Storage migration(s) related to disputes pallet
|
||||||
|
|
||||||
|
use frame_support::traits::StorageVersion;
|
||||||
|
|
||||||
|
/// The current storage version.
|
||||||
|
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
|
||||||
|
|
||||||
|
pub mod v1 {
|
||||||
|
use super::*;
|
||||||
|
use crate::disputes::{Config, Pallet};
|
||||||
|
use frame_support::{
|
||||||
|
pallet_prelude::*, storage_alias, traits::OnRuntimeUpgrade, weights::Weight,
|
||||||
|
};
|
||||||
|
use primitives::v2::SessionIndex;
|
||||||
|
use sp_std::prelude::*;
|
||||||
|
|
||||||
|
#[storage_alias]
|
||||||
|
type SpamSlots<T: Config> = StorageMap<Pallet<T>, Twox64Concat, SessionIndex, Vec<u32>>;
|
||||||
|
|
||||||
|
pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>);
|
||||||
|
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
|
||||||
|
fn on_runtime_upgrade() -> Weight {
|
||||||
|
let mut weight: Weight = Weight::zero();
|
||||||
|
|
||||||
|
if StorageVersion::get::<Pallet<T>>() < STORAGE_VERSION {
|
||||||
|
log::info!(target: crate::disputes::LOG_TARGET, "Migrating disputes storage to v1");
|
||||||
|
weight += migrate_to_v1::<T>();
|
||||||
|
STORAGE_VERSION.put::<Pallet<T>>();
|
||||||
|
weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
|
||||||
|
} else {
|
||||||
|
log::info!(
|
||||||
|
target: crate::disputes::LOG_TARGET,
|
||||||
|
"Disputes storage up to date - no need for migration"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
weight
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn pre_upgrade() -> Result<Vec<u8>, &'static str> {
|
||||||
|
log::trace!(
|
||||||
|
target: crate::disputes::LOG_TARGET,
|
||||||
|
"SpamSlots before migration: {}",
|
||||||
|
SpamSlots::<T>::iter().count()
|
||||||
|
);
|
||||||
|
ensure!(
|
||||||
|
StorageVersion::get::<Pallet<T>>() == 0,
|
||||||
|
"Storage version should be less than `1` before the migration",
|
||||||
|
);
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> {
|
||||||
|
log::trace!(target: crate::disputes::LOG_TARGET, "Running post_upgrade()");
|
||||||
|
ensure!(
|
||||||
|
StorageVersion::get::<Pallet<T>>() == STORAGE_VERSION,
|
||||||
|
"Storage version should be `1` after the migration"
|
||||||
|
);
|
||||||
|
ensure!(
|
||||||
|
SpamSlots::<T>::iter().count() == 0,
|
||||||
|
"SpamSlots should be empty after the migration"
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Migrates the pallet storage to the most recent version, checking and setting the `StorageVersion`.
|
||||||
|
pub fn migrate_to_v1<T: Config>() -> Weight {
|
||||||
|
let mut weight: Weight = Weight::zero();
|
||||||
|
|
||||||
|
// SpamSlots should not contain too many keys so removing everything at once should be safe
|
||||||
|
let res = SpamSlots::<T>::clear(u32::MAX, None);
|
||||||
|
// `loops` is the number of iterations => used to calculate read weights
|
||||||
|
// `backend` is the number of keys removed from the backend => used to calculate write weights
|
||||||
|
weight = weight
|
||||||
|
.saturating_add(T::DbWeight::get().reads_writes(res.loops as u64, res.backend as u64));
|
||||||
|
|
||||||
|
weight
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,6 @@ use crate::{
|
|||||||
Test, PUNISH_VALIDATORS_AGAINST, PUNISH_VALIDATORS_FOR, REWARD_VALIDATORS,
|
Test, PUNISH_VALIDATORS_AGAINST, PUNISH_VALIDATORS_FOR, REWARD_VALIDATORS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use assert_matches::assert_matches;
|
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
assert_err, assert_noop, assert_ok,
|
assert_err, assert_noop, assert_ok,
|
||||||
traits::{OnFinalize, OnInitialize},
|
traits::{OnFinalize, OnInitialize},
|
||||||
@@ -31,20 +30,16 @@ use frame_support::{
|
|||||||
use primitives::v2::BlockNumber;
|
use primitives::v2::BlockNumber;
|
||||||
use sp_core::{crypto::CryptoType, Pair};
|
use sp_core::{crypto::CryptoType, Pair};
|
||||||
|
|
||||||
/// Filtering updates the spam slots, as such update them.
|
fn filter_dispute_set(stmts: MultiDisputeStatementSet) -> CheckedMultiDisputeStatementSet {
|
||||||
fn update_spam_slots(stmts: MultiDisputeStatementSet) -> CheckedMultiDisputeStatementSet {
|
|
||||||
let config = <configuration::Pallet<Test>>::config();
|
let config = <configuration::Pallet<Test>>::config();
|
||||||
let max_spam_slots = config.dispute_max_spam_slots;
|
|
||||||
let post_conclusion_acceptance_period = config.dispute_post_conclusion_acceptance_period;
|
let post_conclusion_acceptance_period = config.dispute_post_conclusion_acceptance_period;
|
||||||
|
|
||||||
stmts
|
stmts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|set| {
|
.filter_map(|set| {
|
||||||
// updates spam slots implicitly
|
|
||||||
let filter = Pallet::<Test>::filter_dispute_data(
|
let filter = Pallet::<Test>::filter_dispute_data(
|
||||||
&set,
|
&set,
|
||||||
post_conclusion_acceptance_period,
|
post_conclusion_acceptance_period,
|
||||||
max_spam_slots,
|
|
||||||
VerifyDisputeSignatures::Skip,
|
VerifyDisputeSignatures::Skip,
|
||||||
);
|
);
|
||||||
filter.filter_statement_set(set)
|
filter.filter_statement_set(set)
|
||||||
@@ -135,7 +130,7 @@ fn test_dispute_state_flag_from_state() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_import_new_participant_spam_inc() {
|
fn test_import_new_participant() {
|
||||||
let mut importer = DisputeStateImporter::new(
|
let mut importer = DisputeStateImporter::new(
|
||||||
DisputeState {
|
DisputeState {
|
||||||
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 0, 0, 0, 0, 0, 0],
|
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 0, 0, 0, 0, 0, 0],
|
||||||
@@ -171,14 +166,13 @@ fn test_import_new_participant_spam_inc() {
|
|||||||
concluded_at: None,
|
concluded_at: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(summary.spam_slot_changes, vec![(ValidatorIndex(2), SpamSlotChange::Inc)]);
|
|
||||||
assert!(summary.slash_for.is_empty());
|
assert!(summary.slash_for.is_empty());
|
||||||
assert!(summary.slash_against.is_empty());
|
assert!(summary.slash_against.is_empty());
|
||||||
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 1, 0, 0, 0, 0, 0]);
|
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 1, 0, 0, 0, 0, 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_import_prev_participant_spam_dec_confirmed() {
|
fn test_import_prev_participant_confirmed() {
|
||||||
let mut importer = DisputeStateImporter::new(
|
let mut importer = DisputeStateImporter::new(
|
||||||
DisputeState {
|
DisputeState {
|
||||||
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 0, 0, 0, 0, 0, 0],
|
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 0, 0, 0, 0, 0, 0],
|
||||||
@@ -201,10 +195,7 @@ fn test_import_prev_participant_spam_dec_confirmed() {
|
|||||||
concluded_at: None,
|
concluded_at: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
summary.spam_slot_changes,
|
|
||||||
vec![(ValidatorIndex(0), SpamSlotChange::Dec), (ValidatorIndex(1), SpamSlotChange::Dec),],
|
|
||||||
);
|
|
||||||
assert!(summary.slash_for.is_empty());
|
assert!(summary.slash_for.is_empty());
|
||||||
assert!(summary.slash_against.is_empty());
|
assert!(summary.slash_against.is_empty());
|
||||||
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 1, 0, 0, 0, 0, 0]);
|
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 1, 0, 0, 0, 0, 0]);
|
||||||
@@ -212,7 +203,7 @@ fn test_import_prev_participant_spam_dec_confirmed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_import_prev_participant_spam_dec_confirmed_slash_for() {
|
fn test_import_prev_participant_confirmed_slash_for() {
|
||||||
let mut importer = DisputeStateImporter::new(
|
let mut importer = DisputeStateImporter::new(
|
||||||
DisputeState {
|
DisputeState {
|
||||||
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 0, 0, 0, 0, 0, 0],
|
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 0, 0, 0, 0, 0, 0],
|
||||||
@@ -240,10 +231,7 @@ fn test_import_prev_participant_spam_dec_confirmed_slash_for() {
|
|||||||
concluded_at: Some(0),
|
concluded_at: Some(0),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
summary.spam_slot_changes,
|
|
||||||
vec![(ValidatorIndex(0), SpamSlotChange::Dec), (ValidatorIndex(1), SpamSlotChange::Dec),],
|
|
||||||
);
|
|
||||||
assert_eq!(summary.slash_for, vec![ValidatorIndex(0), ValidatorIndex(2)]);
|
assert_eq!(summary.slash_for, vec![ValidatorIndex(0), ValidatorIndex(2)]);
|
||||||
assert!(summary.slash_against.is_empty());
|
assert!(summary.slash_against.is_empty());
|
||||||
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 1, 1, 1, 1, 1, 0]);
|
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 1, 1, 1, 1, 1, 0]);
|
||||||
@@ -281,224 +269,12 @@ fn test_import_slash_against() {
|
|||||||
concluded_at: Some(0),
|
concluded_at: Some(0),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert!(summary.spam_slot_changes.is_empty());
|
|
||||||
assert!(summary.slash_for.is_empty());
|
assert!(summary.slash_for.is_empty());
|
||||||
assert_eq!(summary.slash_against, vec![ValidatorIndex(1), ValidatorIndex(5)]);
|
assert_eq!(summary.slash_against, vec![ValidatorIndex(1), ValidatorIndex(5)]);
|
||||||
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 0, 1, 1, 1, 1, 1]);
|
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 0, 1, 1, 1, 1, 1]);
|
||||||
assert_eq!(summary.new_flags, DisputeStateFlags::FOR_SUPERMAJORITY);
|
assert_eq!(summary.new_flags, DisputeStateFlags::FOR_SUPERMAJORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_dispute_statement_set_entry(
|
|
||||||
session: u32,
|
|
||||||
candidate_hash: CandidateHash,
|
|
||||||
statement: DisputeStatement,
|
|
||||||
validator: &<ValidatorId as CryptoType>::Pair,
|
|
||||||
) -> (DisputeStatement, ValidatorSignature) {
|
|
||||||
let valid = match &statement {
|
|
||||||
DisputeStatement::Valid(_) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
let signature_bytes = validator
|
|
||||||
.sign(&ExplicitDisputeStatement { valid, candidate_hash, session }.signing_payload());
|
|
||||||
let signature = ValidatorSignature::try_from(signature_bytes).unwrap();
|
|
||||||
(statement, signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_dispute_statement_set(
|
|
||||||
session: SessionIndex,
|
|
||||||
candidate_hash: CandidateHash,
|
|
||||||
validators: &[<ValidatorId as CryptoType>::Pair],
|
|
||||||
vidxs: Vec<(usize, DisputeStatement)>,
|
|
||||||
) -> DisputeStatementSet {
|
|
||||||
let statements = vidxs
|
|
||||||
.into_iter()
|
|
||||||
.map(|(v_i, statement)| {
|
|
||||||
let validator_index = ValidatorIndex(v_i as u32);
|
|
||||||
let (statement, signature) = generate_dispute_statement_set_entry(
|
|
||||||
session,
|
|
||||||
candidate_hash.clone(),
|
|
||||||
statement,
|
|
||||||
&validators[v_i],
|
|
||||||
);
|
|
||||||
(statement, validator_index, signature)
|
|
||||||
})
|
|
||||||
.collect::<Vec<(DisputeStatement, ValidatorIndex, ValidatorSignature)>>();
|
|
||||||
DisputeStatementSet { candidate_hash: candidate_hash.clone(), session, statements }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dispute_statement_becoming_onesided_due_to_spamslots_is_accepted() {
|
|
||||||
let dispute_conclusion_by_time_out_period = 3;
|
|
||||||
let start = 10;
|
|
||||||
let session = start - 1;
|
|
||||||
let dispute_max_spam_slots = 2;
|
|
||||||
let post_conclusion_acceptance_period = 3;
|
|
||||||
|
|
||||||
let mock_genesis_config = MockGenesisConfig {
|
|
||||||
configuration: crate::configuration::GenesisConfig {
|
|
||||||
config: HostConfiguration {
|
|
||||||
dispute_conclusion_by_time_out_period,
|
|
||||||
dispute_max_spam_slots,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
new_test_ext(mock_genesis_config).execute_with(|| {
|
|
||||||
// We need 6 validators for the byzantine threshold to be 2
|
|
||||||
static ACCOUNT_IDS: &[AccountId] = &[0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
let validators = std::iter::repeat(())
|
|
||||||
.take(7)
|
|
||||||
.map(|_| <ValidatorId as CryptoType>::Pair::generate().0)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let validators = &validators;
|
|
||||||
|
|
||||||
// a new session at each block, but always the same validators
|
|
||||||
let session_change_callback = |block_number: u32| -> Option<NewSession<'_>> {
|
|
||||||
let session_validators =
|
|
||||||
Vec::from_iter(ACCOUNT_IDS.iter().zip(validators.iter().map(|pair| pair.public())));
|
|
||||||
Some((true, block_number, session_validators.clone(), Some(session_validators)))
|
|
||||||
};
|
|
||||||
|
|
||||||
run_to_block(start, session_change_callback);
|
|
||||||
|
|
||||||
// Must be _foreign_ parachain candidate
|
|
||||||
// otherwise slots do not trigger.
|
|
||||||
let candidate_hash_a = CandidateHash(sp_core::H256::repeat_byte(0xA));
|
|
||||||
let candidate_hash_b = CandidateHash(sp_core::H256::repeat_byte(0xB));
|
|
||||||
let candidate_hash_c = CandidateHash(sp_core::H256::repeat_byte(0xC));
|
|
||||||
let candidate_hash_d = CandidateHash(sp_core::H256::repeat_byte(0xD));
|
|
||||||
|
|
||||||
let stmts = vec![
|
|
||||||
// a
|
|
||||||
generate_dispute_statement_set(
|
|
||||||
session,
|
|
||||||
candidate_hash_a,
|
|
||||||
validators,
|
|
||||||
vec![
|
|
||||||
(3, DisputeStatement::Valid(ValidDisputeStatementKind::Explicit)),
|
|
||||||
(6, DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// b
|
|
||||||
generate_dispute_statement_set(
|
|
||||||
session,
|
|
||||||
candidate_hash_b,
|
|
||||||
validators,
|
|
||||||
vec![
|
|
||||||
(1, DisputeStatement::Valid(ValidDisputeStatementKind::Explicit)),
|
|
||||||
(6, DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// c
|
|
||||||
generate_dispute_statement_set(
|
|
||||||
session,
|
|
||||||
candidate_hash_c,
|
|
||||||
validators,
|
|
||||||
vec![
|
|
||||||
(2, DisputeStatement::Valid(ValidDisputeStatementKind::Explicit)),
|
|
||||||
(6, DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// d
|
|
||||||
generate_dispute_statement_set(
|
|
||||||
session,
|
|
||||||
candidate_hash_d,
|
|
||||||
validators,
|
|
||||||
vec![
|
|
||||||
(4, DisputeStatement::Valid(ValidDisputeStatementKind::Explicit)),
|
|
||||||
(5, DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
generate_dispute_statement_set(
|
|
||||||
session,
|
|
||||||
candidate_hash_d,
|
|
||||||
validators,
|
|
||||||
vec![(6, DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit))],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
// no filtering happens, host config for `dispute_max_spam_slots: 2` is the default
|
|
||||||
// updates spam slots implicitly
|
|
||||||
let set = stmts[0].clone();
|
|
||||||
let filter = Pallet::<Test>::filter_dispute_data(
|
|
||||||
&set,
|
|
||||||
post_conclusion_acceptance_period,
|
|
||||||
dispute_max_spam_slots,
|
|
||||||
VerifyDisputeSignatures::Skip,
|
|
||||||
);
|
|
||||||
assert_matches!(&filter, StatementSetFilter::RemoveIndices(v) if v.is_empty());
|
|
||||||
assert_matches!(filter.filter_statement_set(set.clone()), Some(modified) => {
|
|
||||||
assert_eq!(&set, modified.as_ref());
|
|
||||||
});
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(session), Some(vec![0, 0, 0, 1, 0, 0, 1]));
|
|
||||||
|
|
||||||
// <----->
|
|
||||||
|
|
||||||
// 2nd, still ok? Should be
|
|
||||||
let set = stmts[1].clone();
|
|
||||||
let filter = Pallet::<Test>::filter_dispute_data(
|
|
||||||
&set,
|
|
||||||
post_conclusion_acceptance_period,
|
|
||||||
dispute_max_spam_slots,
|
|
||||||
VerifyDisputeSignatures::Skip,
|
|
||||||
);
|
|
||||||
assert_matches!(&filter, StatementSetFilter::RemoveIndices(v) if v.is_empty());
|
|
||||||
assert_matches!(filter.filter_statement_set(set.clone()), Some(modified) => {
|
|
||||||
assert_eq!(&set, modified.as_ref());
|
|
||||||
});
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(session), Some(vec![0, 1, 0, 1, 0, 0, 2]));
|
|
||||||
|
|
||||||
// <----->
|
|
||||||
|
|
||||||
// now this is the third spammy participation of validator 6 and hence
|
|
||||||
let set = stmts[2].clone();
|
|
||||||
let filter = Pallet::<Test>::filter_dispute_data(
|
|
||||||
&set,
|
|
||||||
post_conclusion_acceptance_period,
|
|
||||||
dispute_max_spam_slots,
|
|
||||||
VerifyDisputeSignatures::Skip,
|
|
||||||
);
|
|
||||||
assert_matches!(&filter, StatementSetFilter::RemoveAll);
|
|
||||||
// no need to apply the filter,
|
|
||||||
// we don't do anything with the result, and spam slots were updated already
|
|
||||||
|
|
||||||
// <----->
|
|
||||||
|
|
||||||
// now there is no pariticipation in this dispute being initiated
|
|
||||||
// only validator 4 and 5 are part of it
|
|
||||||
// with 3 validators it's not a an unconfirmed dispute anymore
|
|
||||||
// so validator 6, while being considered spammy should work again
|
|
||||||
let set = stmts[3].clone();
|
|
||||||
let filter = Pallet::<Test>::filter_dispute_data(
|
|
||||||
&set,
|
|
||||||
post_conclusion_acceptance_period,
|
|
||||||
dispute_max_spam_slots,
|
|
||||||
VerifyDisputeSignatures::Skip,
|
|
||||||
);
|
|
||||||
assert_matches!(&filter, StatementSetFilter::RemoveIndices(v) if v.is_empty());
|
|
||||||
// no need to apply the filter,
|
|
||||||
// we don't do anything with the result, and spam slots were updated already
|
|
||||||
|
|
||||||
// <----->
|
|
||||||
|
|
||||||
// it's a spammy participant, so a new dispute will not be accepted being initiated by a spammer
|
|
||||||
let set = stmts[4].clone();
|
|
||||||
let filter = Pallet::<Test>::filter_dispute_data(
|
|
||||||
&set,
|
|
||||||
post_conclusion_acceptance_period,
|
|
||||||
dispute_max_spam_slots,
|
|
||||||
VerifyDisputeSignatures::Skip,
|
|
||||||
);
|
|
||||||
assert_matches!(&filter, StatementSetFilter::RemoveAll);
|
|
||||||
assert_matches!(filter.filter_statement_set(set.clone()), None);
|
|
||||||
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(session), Some(vec![0, 1, 1, 1, 1, 1, 3]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that dispute timeout is handled correctly.
|
// Test that dispute timeout is handled correctly.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dispute_timeout() {
|
fn test_dispute_timeout() {
|
||||||
@@ -517,7 +293,7 @@ fn test_dispute_timeout() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
new_test_ext(mock_genesis_config).execute_with(|| {
|
new_test_ext(mock_genesis_config).execute_with(|| {
|
||||||
// We need 6 validators for the byzantine threshold to be 2
|
// We need 7 validators for the byzantine threshold to be 2
|
||||||
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
|
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
|
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
let v2 = <ValidatorId as CryptoType>::Pair::generate().0;
|
let v2 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
@@ -554,10 +330,12 @@ fn test_dispute_timeout() {
|
|||||||
|
|
||||||
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
|
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
|
||||||
|
|
||||||
// v0 votes for 3, v6 against.
|
// v0 and v1 vote for 3, v2 against. We need f+1 votes (3) so that the dispute is
|
||||||
|
// confirmed. Otherwise It will be filtered out.
|
||||||
|
let session = start - 1;
|
||||||
let stmts = vec![DisputeStatementSet {
|
let stmts = vec![DisputeStatementSet {
|
||||||
candidate_hash: candidate_hash.clone(),
|
candidate_hash: candidate_hash.clone(),
|
||||||
session: start - 1,
|
session,
|
||||||
statements: vec![
|
statements: vec![
|
||||||
(
|
(
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
||||||
@@ -571,6 +349,18 @@ fn test_dispute_timeout() {
|
|||||||
.signing_payload(),
|
.signing_payload(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
||||||
|
ValidatorIndex(1),
|
||||||
|
v1.sign(
|
||||||
|
&ExplicitDisputeStatement {
|
||||||
|
valid: true,
|
||||||
|
candidate_hash: candidate_hash.clone(),
|
||||||
|
session: start - 1,
|
||||||
|
}
|
||||||
|
.signing_payload(),
|
||||||
|
),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
||||||
ValidatorIndex(6),
|
ValidatorIndex(6),
|
||||||
@@ -586,8 +376,7 @@ fn test_dispute_timeout() {
|
|||||||
],
|
],
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let stmts = update_spam_slots(stmts);
|
let stmts = filter_dispute_set(stmts);
|
||||||
assert_eq!(SpamSlots::<Test>::get(start - 1), Some(vec![1, 0, 0, 0, 0, 0, 1]));
|
|
||||||
|
|
||||||
assert_ok!(
|
assert_ok!(
|
||||||
Pallet::<Test>::process_checked_multi_dispute_data(stmts),
|
Pallet::<Test>::process_checked_multi_dispute_data(stmts),
|
||||||
@@ -596,11 +385,20 @@ fn test_dispute_timeout() {
|
|||||||
|
|
||||||
// Run to timeout period
|
// Run to timeout period
|
||||||
run_to_block(start + dispute_conclusion_by_time_out_period, |_| None);
|
run_to_block(start + dispute_conclusion_by_time_out_period, |_| None);
|
||||||
assert_eq!(SpamSlots::<Test>::get(start - 1), Some(vec![1, 0, 0, 0, 0, 0, 1]));
|
assert!(<Disputes<Test>>::get(&session, &candidate_hash)
|
||||||
|
.expect("dispute should exist")
|
||||||
|
.concluded_at
|
||||||
|
.is_none());
|
||||||
|
|
||||||
// Run to timeout + 1 in order to executive on_finalize(timeout)
|
// Run to timeout + 1 in order to executive on_finalize(timeout)
|
||||||
run_to_block(start + dispute_conclusion_by_time_out_period + 1, |_| None);
|
run_to_block(start + dispute_conclusion_by_time_out_period + 1, |_| None);
|
||||||
assert_eq!(SpamSlots::<Test>::get(start - 1), Some(vec![0, 0, 0, 0, 0, 0, 0]));
|
assert_eq!(
|
||||||
|
<Disputes<Test>>::get(&session, &candidate_hash)
|
||||||
|
.expect("dispute should exist")
|
||||||
|
.concluded_at
|
||||||
|
.expect("dispute should have concluded"),
|
||||||
|
start + dispute_conclusion_by_time_out_period + 1
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -888,16 +686,13 @@ fn test_freeze_provided_against_supermajority_for_included() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// tests for:
|
mod unconfirmed_disputes {
|
||||||
// * provide_multi_dispute: with success scenario
|
use super::*;
|
||||||
// * disputes: correctness of datas
|
use assert_matches::assert_matches;
|
||||||
// * could_be_invalid: correctness of datas
|
use sp_runtime::ModuleError;
|
||||||
// * note_included: decrement spam correctly
|
|
||||||
// * spam slots: correctly incremented and decremented
|
// Shared initialization code between `test_unconfirmed_are_ignored` and `test_unconfirmed_disputes_cause_block_import_error`
|
||||||
// * ensure rewards and punishment are correctly called.
|
fn generate_dispute_statement_set_and_run_to_block() -> DisputeStatementSet {
|
||||||
#[test]
|
|
||||||
fn test_provide_multi_dispute_success_and_other() {
|
|
||||||
new_test_ext(Default::default()).execute_with(|| {
|
|
||||||
// 7 validators needed for byzantine threshold of 2.
|
// 7 validators needed for byzantine threshold of 2.
|
||||||
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
|
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
|
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
@@ -907,6 +702,7 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
let v5 = <ValidatorId as CryptoType>::Pair::generate().0;
|
let v5 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
let v6 = <ValidatorId as CryptoType>::Pair::generate().0;
|
let v6 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
|
|
||||||
|
// Mapping between key pair and `ValidatorIndex`
|
||||||
// v0 -> 0
|
// v0 -> 0
|
||||||
// v1 -> 3
|
// v1 -> 3
|
||||||
// v2 -> 6
|
// v2 -> 6
|
||||||
@@ -943,7 +739,120 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
|
|
||||||
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
|
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
|
||||||
|
|
||||||
// v0 votes for 3, v6 votes against
|
// v0 votes for 4, v1 votes against 4.
|
||||||
|
DisputeStatementSet {
|
||||||
|
candidate_hash: candidate_hash.clone(),
|
||||||
|
session: 4,
|
||||||
|
statements: vec![
|
||||||
|
(
|
||||||
|
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
||||||
|
ValidatorIndex(0),
|
||||||
|
v0.sign(
|
||||||
|
&ExplicitDisputeStatement {
|
||||||
|
valid: true,
|
||||||
|
candidate_hash: candidate_hash.clone(),
|
||||||
|
session: 4,
|
||||||
|
}
|
||||||
|
.signing_payload(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
||||||
|
ValidatorIndex(3),
|
||||||
|
v1.sign(
|
||||||
|
&ExplicitDisputeStatement {
|
||||||
|
valid: false,
|
||||||
|
candidate_hash: candidate_hash.clone(),
|
||||||
|
session: 4,
|
||||||
|
}
|
||||||
|
.signing_payload(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_unconfirmed_are_ignored() {
|
||||||
|
new_test_ext(Default::default()).execute_with(|| {
|
||||||
|
let stmts = vec![generate_dispute_statement_set_and_run_to_block()];
|
||||||
|
let stmts = filter_dispute_set(stmts);
|
||||||
|
|
||||||
|
// Not confirmed => should be filtered out
|
||||||
|
assert_ok!(Pallet::<Test>::process_checked_multi_dispute_data(stmts), vec![],);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unconfirmed_disputes_cause_block_import_error() {
|
||||||
|
new_test_ext(Default::default()).execute_with(|| {
|
||||||
|
|
||||||
|
let stmts = generate_dispute_statement_set_and_run_to_block();
|
||||||
|
let stmts = vec![CheckedDisputeStatementSet::unchecked_from_unchecked(stmts)];
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
Pallet::<Test>::process_checked_multi_dispute_data(stmts),
|
||||||
|
Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("UnconfirmedDispute"))
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests for:
|
||||||
|
// * provide_multi_dispute: with success scenario
|
||||||
|
// * disputes: correctness of datas
|
||||||
|
// * could_be_invalid: correctness of datas
|
||||||
|
// * ensure rewards and punishment are correctly called.
|
||||||
|
#[test]
|
||||||
|
fn test_provide_multi_dispute_success_and_other() {
|
||||||
|
new_test_ext(Default::default()).execute_with(|| {
|
||||||
|
// 7 validators needed for byzantine threshold of 2.
|
||||||
|
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
|
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
|
let v2 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
|
let v3 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
|
let v4 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
|
let v5 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
|
let v6 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
|
|
||||||
|
// Mapping between key pair and `ValidatorIndex`
|
||||||
|
// v0 -> 0
|
||||||
|
// v1 -> 3
|
||||||
|
// v2 -> 6
|
||||||
|
// v3 -> 5
|
||||||
|
// v4 -> 1
|
||||||
|
// v5 -> 4
|
||||||
|
// v6 -> 2
|
||||||
|
|
||||||
|
run_to_block(6, |b| {
|
||||||
|
// a new session at each block
|
||||||
|
Some((
|
||||||
|
true,
|
||||||
|
b,
|
||||||
|
vec![
|
||||||
|
(&0, v0.public()),
|
||||||
|
(&1, v1.public()),
|
||||||
|
(&2, v2.public()),
|
||||||
|
(&3, v3.public()),
|
||||||
|
(&4, v4.public()),
|
||||||
|
(&5, v5.public()),
|
||||||
|
(&6, v6.public()),
|
||||||
|
],
|
||||||
|
Some(vec![
|
||||||
|
(&0, v0.public()),
|
||||||
|
(&1, v1.public()),
|
||||||
|
(&2, v2.public()),
|
||||||
|
(&3, v3.public()),
|
||||||
|
(&4, v4.public()),
|
||||||
|
(&5, v5.public()),
|
||||||
|
(&6, v6.public()),
|
||||||
|
]),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
|
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
|
||||||
|
|
||||||
|
// v0 and v1 vote for 3, v6 votes against
|
||||||
let stmts = vec![DisputeStatementSet {
|
let stmts = vec![DisputeStatementSet {
|
||||||
candidate_hash: candidate_hash.clone(),
|
candidate_hash: candidate_hash.clone(),
|
||||||
session: 3,
|
session: 3,
|
||||||
@@ -972,53 +881,7 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
.signing_payload(),
|
.signing_payload(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
(
|
||||||
}];
|
|
||||||
|
|
||||||
let stmts = update_spam_slots(stmts);
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(3), Some(vec![1, 0, 1, 0, 0, 0, 0]));
|
|
||||||
|
|
||||||
assert_ok!(
|
|
||||||
Pallet::<Test>::process_checked_multi_dispute_data(stmts),
|
|
||||||
vec![(3, candidate_hash.clone())],
|
|
||||||
);
|
|
||||||
|
|
||||||
// v1 votes for 4 and for 3, v6 votes against 4.
|
|
||||||
let stmts = vec![
|
|
||||||
DisputeStatementSet {
|
|
||||||
candidate_hash: candidate_hash.clone(),
|
|
||||||
session: 4,
|
|
||||||
statements: vec![
|
|
||||||
(
|
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(3),
|
|
||||||
v1.sign(
|
|
||||||
&ExplicitDisputeStatement {
|
|
||||||
valid: true,
|
|
||||||
candidate_hash: candidate_hash.clone(),
|
|
||||||
session: 4,
|
|
||||||
}
|
|
||||||
.signing_payload(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(2),
|
|
||||||
v6.sign(
|
|
||||||
&ExplicitDisputeStatement {
|
|
||||||
valid: false,
|
|
||||||
candidate_hash: candidate_hash.clone(),
|
|
||||||
session: 4,
|
|
||||||
}
|
|
||||||
.signing_payload(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
DisputeStatementSet {
|
|
||||||
candidate_hash: candidate_hash.clone(),
|
|
||||||
session: 3,
|
|
||||||
statements: vec![(
|
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
||||||
ValidatorIndex(3),
|
ValidatorIndex(3),
|
||||||
v1.sign(
|
v1.sign(
|
||||||
@@ -1029,20 +892,18 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
}
|
}
|
||||||
.signing_payload(),
|
.signing_payload(),
|
||||||
),
|
),
|
||||||
)],
|
),
|
||||||
},
|
],
|
||||||
];
|
}];
|
||||||
|
|
||||||
let stmts = update_spam_slots(stmts);
|
let stmts = filter_dispute_set(stmts);
|
||||||
|
|
||||||
assert_ok!(
|
assert_ok!(
|
||||||
Pallet::<Test>::process_checked_multi_dispute_data(stmts),
|
Pallet::<Test>::process_checked_multi_dispute_data(stmts),
|
||||||
vec![(4, candidate_hash.clone())],
|
vec![(3, candidate_hash.clone())],
|
||||||
);
|
);
|
||||||
assert_eq!(SpamSlots::<Test>::get(3), Some(vec![0, 0, 0, 0, 0, 0, 0])); // Confirmed as no longer spam
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(4), Some(vec![0, 0, 1, 1, 0, 0, 0]));
|
|
||||||
|
|
||||||
// v3 votes against 3 and for 5, v6 votes against 5.
|
// v3 votes against 3 and for 5, v2 and v6 vote against 5.
|
||||||
let stmts = vec![
|
let stmts = vec![
|
||||||
DisputeStatementSet {
|
DisputeStatementSet {
|
||||||
candidate_hash: candidate_hash.clone(),
|
candidate_hash: candidate_hash.clone(),
|
||||||
@@ -1076,6 +937,18 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
.signing_payload(),
|
.signing_payload(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
||||||
|
ValidatorIndex(6),
|
||||||
|
v2.sign(
|
||||||
|
&ExplicitDisputeStatement {
|
||||||
|
valid: false,
|
||||||
|
candidate_hash: candidate_hash.clone(),
|
||||||
|
session: 5,
|
||||||
|
}
|
||||||
|
.signing_payload(),
|
||||||
|
),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
||||||
ValidatorIndex(2),
|
ValidatorIndex(2),
|
||||||
@@ -1092,55 +965,31 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let stmts = update_spam_slots(stmts);
|
let stmts = filter_dispute_set(stmts);
|
||||||
assert_ok!(
|
assert_ok!(
|
||||||
Pallet::<Test>::process_checked_multi_dispute_data(stmts),
|
Pallet::<Test>::process_checked_multi_dispute_data(stmts),
|
||||||
vec![(5, candidate_hash.clone())],
|
vec![(5, candidate_hash.clone())],
|
||||||
);
|
);
|
||||||
assert_eq!(SpamSlots::<Test>::get(3), Some(vec![0, 0, 0, 0, 0, 0, 0]));
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(4), Some(vec![0, 0, 1, 1, 0, 0, 0]));
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(5), Some(vec![0, 0, 1, 0, 0, 1, 0]));
|
|
||||||
|
|
||||||
// v2 votes for 3 and against 5
|
// v2 votes for 3
|
||||||
let stmts = vec![
|
let stmts = vec![DisputeStatementSet {
|
||||||
DisputeStatementSet {
|
candidate_hash: candidate_hash.clone(),
|
||||||
candidate_hash: candidate_hash.clone(),
|
session: 3,
|
||||||
session: 3,
|
statements: vec![(
|
||||||
statements: vec![(
|
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
ValidatorIndex(6),
|
||||||
ValidatorIndex(6),
|
v2.sign(
|
||||||
v2.sign(
|
&ExplicitDisputeStatement {
|
||||||
&ExplicitDisputeStatement {
|
valid: true,
|
||||||
valid: true,
|
candidate_hash: candidate_hash.clone(),
|
||||||
candidate_hash: candidate_hash.clone(),
|
session: 3,
|
||||||
session: 3,
|
}
|
||||||
}
|
.signing_payload(),
|
||||||
.signing_payload(),
|
),
|
||||||
),
|
)],
|
||||||
)],
|
}];
|
||||||
},
|
let stmts = filter_dispute_set(stmts);
|
||||||
DisputeStatementSet {
|
|
||||||
candidate_hash: candidate_hash.clone(),
|
|
||||||
session: 5,
|
|
||||||
statements: vec![(
|
|
||||||
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(6),
|
|
||||||
v2.sign(
|
|
||||||
&ExplicitDisputeStatement {
|
|
||||||
valid: false,
|
|
||||||
candidate_hash: candidate_hash.clone(),
|
|
||||||
session: 5,
|
|
||||||
}
|
|
||||||
.signing_payload(),
|
|
||||||
),
|
|
||||||
)],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let stmts = update_spam_slots(stmts);
|
|
||||||
assert_ok!(Pallet::<Test>::process_checked_multi_dispute_data(stmts), vec![]);
|
assert_ok!(Pallet::<Test>::process_checked_multi_dispute_data(stmts), vec![]);
|
||||||
assert_eq!(SpamSlots::<Test>::get(3), Some(vec![0, 0, 0, 0, 0, 0, 0]));
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(4), Some(vec![0, 0, 1, 1, 0, 0, 0]));
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(5), Some(vec![0, 0, 0, 0, 0, 0, 0]));
|
|
||||||
|
|
||||||
let stmts = vec![
|
let stmts = vec![
|
||||||
// 0, 4, and 5 vote against 5
|
// 0, 4, and 5 vote against 5
|
||||||
@@ -1218,7 +1067,7 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
let stmts = update_spam_slots(stmts);
|
let stmts = filter_dispute_set(stmts);
|
||||||
assert_ok!(Pallet::<Test>::process_checked_multi_dispute_data(stmts), vec![]);
|
assert_ok!(Pallet::<Test>::process_checked_multi_dispute_data(stmts), vec![]);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1244,16 +1093,6 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
concluded_at: Some(6), // 5 vote for
|
concluded_at: Some(6), // 5 vote for
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
(
|
|
||||||
4,
|
|
||||||
candidate_hash.clone(),
|
|
||||||
DisputeState {
|
|
||||||
validators_for: bitvec![u8, BitOrderLsb0; 0, 0, 0, 1, 0, 0, 0],
|
|
||||||
validators_against: bitvec![u8, BitOrderLsb0; 0, 0, 1, 0, 0, 0, 0],
|
|
||||||
start: 6,
|
|
||||||
concluded_at: None,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1261,22 +1100,14 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
assert!(!Pallet::<Test>::concluded_invalid(4, candidate_hash.clone()));
|
assert!(!Pallet::<Test>::concluded_invalid(4, candidate_hash.clone()));
|
||||||
assert!(Pallet::<Test>::concluded_invalid(5, candidate_hash.clone()));
|
assert!(Pallet::<Test>::concluded_invalid(5, candidate_hash.clone()));
|
||||||
|
|
||||||
// Ensure inclusion removes spam slots
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(4), Some(vec![0, 0, 1, 1, 0, 0, 0]));
|
|
||||||
Pallet::<Test>::note_included(4, candidate_hash.clone(), 4);
|
|
||||||
assert_eq!(SpamSlots::<Test>::get(4), Some(vec![0, 0, 0, 0, 0, 0, 0]));
|
|
||||||
|
|
||||||
// Ensure the `reward_validator` function was correctly called
|
// Ensure the `reward_validator` function was correctly called
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
REWARD_VALIDATORS.with(|r| r.borrow().clone()),
|
REWARD_VALIDATORS.with(|r| r.borrow().clone()),
|
||||||
vec![
|
vec![
|
||||||
(3, vec![ValidatorIndex(0), ValidatorIndex(2)]),
|
(3, vec![ValidatorIndex(0), ValidatorIndex(2), ValidatorIndex(3)]),
|
||||||
(4, vec![ValidatorIndex(2), ValidatorIndex(3)]),
|
|
||||||
(3, vec![ValidatorIndex(3)]),
|
|
||||||
(3, vec![ValidatorIndex(5)]),
|
(3, vec![ValidatorIndex(5)]),
|
||||||
(5, vec![ValidatorIndex(2), ValidatorIndex(5)]),
|
(5, vec![ValidatorIndex(2), ValidatorIndex(5), ValidatorIndex(6)]),
|
||||||
(3, vec![ValidatorIndex(6)]),
|
(3, vec![ValidatorIndex(6)]),
|
||||||
(5, vec![ValidatorIndex(6)]),
|
|
||||||
(5, vec![ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(4)]),
|
(5, vec![ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(4)]),
|
||||||
(3, vec![ValidatorIndex(1), ValidatorIndex(4)]),
|
(3, vec![ValidatorIndex(1), ValidatorIndex(4)]),
|
||||||
],
|
],
|
||||||
@@ -1286,14 +1117,11 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
PUNISH_VALIDATORS_AGAINST.with(|r| r.borrow().clone()),
|
PUNISH_VALIDATORS_AGAINST.with(|r| r.borrow().clone()),
|
||||||
vec![
|
vec![
|
||||||
(3, vec![]),
|
|
||||||
(4, vec![]),
|
|
||||||
(3, vec![]),
|
(3, vec![]),
|
||||||
(3, vec![]),
|
(3, vec![]),
|
||||||
(5, vec![]),
|
(5, vec![]),
|
||||||
(3, vec![]),
|
(3, vec![]),
|
||||||
(5, vec![]),
|
(5, vec![]),
|
||||||
(5, vec![]),
|
|
||||||
(3, vec![ValidatorIndex(2), ValidatorIndex(5)]),
|
(3, vec![ValidatorIndex(2), ValidatorIndex(5)]),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -1302,13 +1130,10 @@ fn test_provide_multi_dispute_success_and_other() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()),
|
PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()),
|
||||||
vec![
|
vec![
|
||||||
(3, vec![]),
|
|
||||||
(4, vec![]),
|
|
||||||
(3, vec![]),
|
(3, vec![]),
|
||||||
(3, vec![]),
|
(3, vec![]),
|
||||||
(5, vec![]),
|
(5, vec![]),
|
||||||
(3, vec![]),
|
(3, vec![]),
|
||||||
(5, vec![]),
|
|
||||||
(5, vec![ValidatorIndex(5)]),
|
(5, vec![ValidatorIndex(5)]),
|
||||||
(3, vec![]),
|
(3, vec![]),
|
||||||
],
|
],
|
||||||
@@ -1380,44 +1205,6 @@ fn test_has_supermajority_against() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_decrement_spam() {
|
|
||||||
let original_spam_slots = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
|
|
||||||
// Test confirm is no-op
|
|
||||||
let mut spam_slots = original_spam_slots.clone();
|
|
||||||
let dispute_state_confirm = DisputeState {
|
|
||||||
validators_for: bitvec![u8, BitOrderLsb0; 1, 1, 0, 0, 0, 0, 0, 0],
|
|
||||||
validators_against: bitvec![u8, BitOrderLsb0; 1, 0, 1, 0, 0, 0, 0, 0],
|
|
||||||
start: 0,
|
|
||||||
concluded_at: None,
|
|
||||||
};
|
|
||||||
assert_eq!(DisputeStateFlags::from_state(&dispute_state_confirm), DisputeStateFlags::CONFIRMED);
|
|
||||||
assert_eq!(
|
|
||||||
decrement_spam(spam_slots.as_mut(), &dispute_state_confirm),
|
|
||||||
bitvec![u8, BitOrderLsb0; 1, 1, 1, 0, 0, 0, 0, 0],
|
|
||||||
);
|
|
||||||
assert_eq!(spam_slots, original_spam_slots);
|
|
||||||
|
|
||||||
// Test not confirm is decreasing spam
|
|
||||||
let mut spam_slots = original_spam_slots.clone();
|
|
||||||
let dispute_state_no_confirm = DisputeState {
|
|
||||||
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
validators_against: bitvec![u8, BitOrderLsb0; 1, 0, 1, 0, 0, 0, 0, 0],
|
|
||||||
start: 0,
|
|
||||||
concluded_at: None,
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
DisputeStateFlags::from_state(&dispute_state_no_confirm),
|
|
||||||
DisputeStateFlags::default()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
decrement_spam(spam_slots.as_mut(), &dispute_state_no_confirm),
|
|
||||||
bitvec![u8, BitOrderLsb0; 1, 0, 1, 0, 0, 0, 0, 0],
|
|
||||||
);
|
|
||||||
assert_eq!(spam_slots, vec![0, 1, 1, 3, 4, 5, 6, 7]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_signature() {
|
fn test_check_signature() {
|
||||||
let validator_id = <ValidatorId as CryptoType>::Pair::generate().0;
|
let validator_id = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
@@ -1912,14 +1699,12 @@ fn apply_filter_all<T: Config, I: IntoIterator<Item = DisputeStatementSet>>(
|
|||||||
sets: I,
|
sets: I,
|
||||||
) -> Vec<CheckedDisputeStatementSet> {
|
) -> Vec<CheckedDisputeStatementSet> {
|
||||||
let config = <configuration::Pallet<T>>::config();
|
let config = <configuration::Pallet<T>>::config();
|
||||||
let max_spam_slots = config.dispute_max_spam_slots;
|
|
||||||
let post_conclusion_acceptance_period = config.dispute_post_conclusion_acceptance_period;
|
let post_conclusion_acceptance_period = config.dispute_post_conclusion_acceptance_period;
|
||||||
|
|
||||||
let mut acc = Vec::<CheckedDisputeStatementSet>::new();
|
let mut acc = Vec::<CheckedDisputeStatementSet>::new();
|
||||||
for dispute_statement in sets {
|
for dispute_statement in sets {
|
||||||
if let Some(checked) = <Pallet<T> as DisputesHandler<<T>::BlockNumber>>::filter_dispute_data(
|
if let Some(checked) = <Pallet<T> as DisputesHandler<<T>::BlockNumber>>::filter_dispute_data(
|
||||||
dispute_statement,
|
dispute_statement,
|
||||||
max_spam_slots,
|
|
||||||
post_conclusion_acceptance_period,
|
post_conclusion_acceptance_period,
|
||||||
VerifyDisputeSignatures::Yes,
|
VerifyDisputeSignatures::Yes,
|
||||||
) {
|
) {
|
||||||
@@ -1993,13 +1778,11 @@ fn filter_removes_duplicates_within_set() {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_spam_slots = 10;
|
|
||||||
let post_conclusion_acceptance_period = 10;
|
let post_conclusion_acceptance_period = 10;
|
||||||
let statements = <Pallet<Test> as DisputesHandler<
|
let statements = <Pallet<Test> as DisputesHandler<
|
||||||
<Test as frame_system::Config>::BlockNumber,
|
<Test as frame_system::Config>::BlockNumber,
|
||||||
>>::filter_dispute_data(
|
>>::filter_dispute_data(
|
||||||
statements,
|
statements,
|
||||||
max_spam_slots,
|
|
||||||
post_conclusion_acceptance_period,
|
post_conclusion_acceptance_period,
|
||||||
VerifyDisputeSignatures::Yes,
|
VerifyDisputeSignatures::Yes,
|
||||||
);
|
);
|
||||||
@@ -2085,151 +1868,6 @@ fn filter_bad_signatures_correctly_detects_single_sided() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn filter_correctly_accounts_spam_slots() {
|
|
||||||
let dispute_max_spam_slots = 2;
|
|
||||||
|
|
||||||
let mock_genesis_config = MockGenesisConfig {
|
|
||||||
configuration: crate::configuration::GenesisConfig {
|
|
||||||
config: HostConfiguration { dispute_max_spam_slots, ..Default::default() },
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
new_test_ext(mock_genesis_config).execute_with(|| {
|
|
||||||
// We need 7 validators for the byzantine threshold to be 2
|
|
||||||
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
|
|
||||||
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
|
|
||||||
let v2 = <ValidatorId as CryptoType>::Pair::generate().0;
|
|
||||||
let v3 = <ValidatorId as CryptoType>::Pair::generate().0;
|
|
||||||
let v4 = <ValidatorId as CryptoType>::Pair::generate().0;
|
|
||||||
let v5 = <ValidatorId as CryptoType>::Pair::generate().0;
|
|
||||||
let v6 = <ValidatorId as CryptoType>::Pair::generate().0;
|
|
||||||
|
|
||||||
run_to_block(3, |b| {
|
|
||||||
// a new session at each block
|
|
||||||
Some((
|
|
||||||
true,
|
|
||||||
b,
|
|
||||||
vec![
|
|
||||||
(&0, v0.public()),
|
|
||||||
(&1, v1.public()),
|
|
||||||
(&2, v2.public()),
|
|
||||||
(&3, v3.public()),
|
|
||||||
(&4, v4.public()),
|
|
||||||
(&5, v5.public()),
|
|
||||||
(&6, v6.public()),
|
|
||||||
],
|
|
||||||
Some(vec![
|
|
||||||
(&0, v0.public()),
|
|
||||||
(&1, v1.public()),
|
|
||||||
(&2, v2.public()),
|
|
||||||
(&3, v3.public()),
|
|
||||||
(&4, v4.public()),
|
|
||||||
(&5, v5.public()),
|
|
||||||
(&6, v6.public()),
|
|
||||||
]),
|
|
||||||
))
|
|
||||||
});
|
|
||||||
|
|
||||||
let candidate_hash_a = CandidateHash(sp_core::H256::repeat_byte(1));
|
|
||||||
let candidate_hash_b = CandidateHash(sp_core::H256::repeat_byte(2));
|
|
||||||
let candidate_hash_c = CandidateHash(sp_core::H256::repeat_byte(3));
|
|
||||||
|
|
||||||
let payload = |c_hash: &CandidateHash, valid| {
|
|
||||||
ExplicitDisputeStatement { valid, candidate_hash: c_hash.clone(), session: 1 }
|
|
||||||
.signing_payload()
|
|
||||||
};
|
|
||||||
|
|
||||||
let payload_a = payload(&candidate_hash_a, true);
|
|
||||||
let payload_b = payload(&candidate_hash_b, true);
|
|
||||||
let payload_c = payload(&candidate_hash_c, true);
|
|
||||||
|
|
||||||
let payload_a_bad = payload(&candidate_hash_a, false);
|
|
||||||
let payload_b_bad = payload(&candidate_hash_b, false);
|
|
||||||
let payload_c_bad = payload(&candidate_hash_c, false);
|
|
||||||
|
|
||||||
let sig_0a = v0.sign(&payload_a);
|
|
||||||
let sig_0b = v0.sign(&payload_b);
|
|
||||||
let sig_0c = v0.sign(&payload_c);
|
|
||||||
|
|
||||||
let sig_1b = v1.sign(&payload_b);
|
|
||||||
|
|
||||||
let sig_2a = v2.sign(&payload_a_bad);
|
|
||||||
let sig_2b = v2.sign(&payload_b_bad);
|
|
||||||
let sig_2c = v2.sign(&payload_c_bad);
|
|
||||||
|
|
||||||
let statements = vec![
|
|
||||||
// validators 0 and 2 get 1 spam slot from this.
|
|
||||||
DisputeStatementSet {
|
|
||||||
candidate_hash: candidate_hash_a.clone(),
|
|
||||||
session: 1,
|
|
||||||
statements: vec![
|
|
||||||
(
|
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(0),
|
|
||||||
sig_0a.clone(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(6),
|
|
||||||
sig_2a.clone(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// Validators 0, 2, and 3 get no spam slots for this
|
|
||||||
DisputeStatementSet {
|
|
||||||
candidate_hash: candidate_hash_b.clone(),
|
|
||||||
session: 1,
|
|
||||||
statements: vec![
|
|
||||||
(
|
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(0),
|
|
||||||
sig_0b.clone(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(3),
|
|
||||||
sig_1b.clone(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(6),
|
|
||||||
sig_2b.clone(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// Validators 0 and 2 get an extra spam slot for this.
|
|
||||||
DisputeStatementSet {
|
|
||||||
candidate_hash: candidate_hash_c.clone(),
|
|
||||||
session: 1,
|
|
||||||
statements: vec![
|
|
||||||
(
|
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(0),
|
|
||||||
sig_0c.clone(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(6),
|
|
||||||
sig_2c.clone(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
let old_statements = statements
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.map(CheckedDisputeStatementSet::unchecked_from_unchecked)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let statements = apply_filter_all::<Test, _>(statements);
|
|
||||||
|
|
||||||
assert_eq!(statements, old_statements);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn filter_removes_session_out_of_bounds() {
|
fn filter_removes_session_out_of_bounds() {
|
||||||
new_test_ext(Default::default()).execute_with(|| {
|
new_test_ext(Default::default()).execute_with(|| {
|
||||||
|
|||||||
@@ -349,7 +349,6 @@ impl<T: Config> Pallet<T> {
|
|||||||
let (checked_disputes, total_consumed_weight) = {
|
let (checked_disputes, total_consumed_weight) = {
|
||||||
// Obtain config params..
|
// Obtain config params..
|
||||||
let config = <configuration::Pallet<T>>::config();
|
let config = <configuration::Pallet<T>>::config();
|
||||||
let max_spam_slots = config.dispute_max_spam_slots;
|
|
||||||
let post_conclusion_acceptance_period =
|
let post_conclusion_acceptance_period =
|
||||||
config.dispute_post_conclusion_acceptance_period;
|
config.dispute_post_conclusion_acceptance_period;
|
||||||
|
|
||||||
@@ -363,7 +362,6 @@ impl<T: Config> Pallet<T> {
|
|||||||
let dispute_set_validity_check = move |set| {
|
let dispute_set_validity_check = move |set| {
|
||||||
T::DisputesHandler::filter_dispute_data(
|
T::DisputesHandler::filter_dispute_data(
|
||||||
set,
|
set,
|
||||||
max_spam_slots,
|
|
||||||
post_conclusion_acceptance_period,
|
post_conclusion_acceptance_period,
|
||||||
verify_dispute_sigs,
|
verify_dispute_sigs,
|
||||||
)
|
)
|
||||||
@@ -595,7 +593,6 @@ impl<T: Config> Pallet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let config = <configuration::Pallet<T>>::config();
|
let config = <configuration::Pallet<T>>::config();
|
||||||
let max_spam_slots = config.dispute_max_spam_slots;
|
|
||||||
let post_conclusion_acceptance_period = config.dispute_post_conclusion_acceptance_period;
|
let post_conclusion_acceptance_period = config.dispute_post_conclusion_acceptance_period;
|
||||||
|
|
||||||
// TODO: Better if we can convert this to `with_transactional` and handle an error if
|
// TODO: Better if we can convert this to `with_transactional` and handle an error if
|
||||||
@@ -609,7 +606,6 @@ impl<T: Config> Pallet<T> {
|
|||||||
let dispute_statement_set_valid = move |set: DisputeStatementSet| {
|
let dispute_statement_set_valid = move |set: DisputeStatementSet| {
|
||||||
T::DisputesHandler::filter_dispute_data(
|
T::DisputesHandler::filter_dispute_data(
|
||||||
set,
|
set,
|
||||||
max_spam_slots,
|
|
||||||
post_conclusion_acceptance_period,
|
post_conclusion_acceptance_period,
|
||||||
// `DisputeCoordinator` on the node side only forwards
|
// `DisputeCoordinator` on the node side only forwards
|
||||||
// valid dispute statement sets and hence this does not
|
// valid dispute statement sets and hence this does not
|
||||||
|
|||||||
@@ -1605,6 +1605,8 @@ pub type Migrations = (
|
|||||||
crowdloan::migration::MigrateToTrackInactive<Runtime>,
|
crowdloan::migration::MigrateToTrackInactive<Runtime>,
|
||||||
pallet_scheduler::migration::v4::CleanupAgendas<Runtime>,
|
pallet_scheduler::migration::v4::CleanupAgendas<Runtime>,
|
||||||
pallet_staking::migrations::v13::MigrateToV13<Runtime>,
|
pallet_staking::migrations::v13::MigrateToV13<Runtime>,
|
||||||
|
parachains_disputes::migration::v1::MigrateToV1<Runtime>,
|
||||||
|
parachains_configuration::migration::v4::MigrateToV4<Runtime>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Unchecked extrinsic type as expected by this runtime.
|
/// Unchecked extrinsic type as expected by this runtime.
|
||||||
|
|||||||
@@ -1484,6 +1484,8 @@ pub type Migrations = (
|
|||||||
pallet_balances::migration::MigrateToTrackInactive<Runtime, xcm_config::CheckAccount>,
|
pallet_balances::migration::MigrateToTrackInactive<Runtime, xcm_config::CheckAccount>,
|
||||||
crowdloan::migration::MigrateToTrackInactive<Runtime>,
|
crowdloan::migration::MigrateToTrackInactive<Runtime>,
|
||||||
pallet_scheduler::migration::v4::CleanupAgendas<Runtime>,
|
pallet_scheduler::migration::v4::CleanupAgendas<Runtime>,
|
||||||
|
parachains_disputes::migration::v1::MigrateToV1<Runtime>,
|
||||||
|
parachains_configuration::migration::v4::MigrateToV4<Runtime>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Executive: handles dispatch to the various modules.
|
/// Executive: handles dispatch to the various modules.
|
||||||
|
|||||||
@@ -1219,6 +1219,8 @@ pub type Migrations = (
|
|||||||
crowdloan::migration::MigrateToTrackInactive<Runtime>,
|
crowdloan::migration::MigrateToTrackInactive<Runtime>,
|
||||||
pallet_scheduler::migration::v4::CleanupAgendas<Runtime>,
|
pallet_scheduler::migration::v4::CleanupAgendas<Runtime>,
|
||||||
pallet_staking::migrations::v13::MigrateToV13<Runtime>,
|
pallet_staking::migrations::v13::MigrateToV13<Runtime>,
|
||||||
|
parachains_disputes::migration::v1::MigrateToV1<Runtime>,
|
||||||
|
parachains_configuration::migration::v4::MigrateToV4<Runtime>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Unchecked extrinsic type as expected by this runtime.
|
/// Unchecked extrinsic type as expected by this runtime.
|
||||||
|
|||||||
Reference in New Issue
Block a user