mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 10:31:03 +00:00
Fix incomplete sorting. (#4795)
* Fix incomplete sorting. * fmt. * Better test. * Update runtime/parachains/src/disputes.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * chore fmt * simplify the sorting for two items * add test for assure duplicates are detected * fixup tests Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>
This commit is contained in:
@@ -130,7 +130,8 @@ where
|
|||||||
(None, Some(_)) => Ordering::Greater,
|
(None, Some(_)) => Ordering::Greater,
|
||||||
(Some(_), None) => Ordering::Less,
|
(Some(_), None) => Ordering::Less,
|
||||||
// For local disputes, prioritize those that occur at an earlier height.
|
// For local disputes, prioritize those that occur at an earlier height.
|
||||||
(Some(a_height), Some(b_height)) => a_height.cmp(&b_height),
|
(Some(a_height), Some(b_height)) =>
|
||||||
|
a_height.cmp(&b_height).then_with(|| a.candidate_hash.cmp(&b.candidate_hash)),
|
||||||
// Prioritize earlier remote disputes using session as rough proxy.
|
// Prioritize earlier remote disputes using session as rough proxy.
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
let session_ord = a.session.cmp(&b.session);
|
let session_ord = a.session.cmp(&b.session);
|
||||||
@@ -146,6 +147,34 @@ where
|
|||||||
|
|
||||||
use super::paras_inherent::IsSortedBy;
|
use super::paras_inherent::IsSortedBy;
|
||||||
|
|
||||||
|
/// Returns `true` if duplicate items were found, otherwise `false`.
|
||||||
|
///
|
||||||
|
/// `check_equal(a: &T, b: &T)` _must_ return `true`, iff `a` and `b` are equal, otherwise `false.
|
||||||
|
/// The definition of _equal_ is to be defined by the user.
|
||||||
|
///
|
||||||
|
/// Attention: Requires the input `iter` to be sorted, such that _equals_
|
||||||
|
/// would be adjacent in respect whatever `check_equal` defines as equality!
|
||||||
|
fn contains_duplicates_in_sorted_iter<
|
||||||
|
'a,
|
||||||
|
T: 'a,
|
||||||
|
I: 'a + IntoIterator<Item = &'a T>,
|
||||||
|
C: 'static + FnMut(&T, &T) -> bool,
|
||||||
|
>(
|
||||||
|
iter: I,
|
||||||
|
mut check_equal: C,
|
||||||
|
) -> bool {
|
||||||
|
let mut iter = iter.into_iter();
|
||||||
|
if let Some(mut previous) = iter.next() {
|
||||||
|
while let Some(current) = iter.next() {
|
||||||
|
if check_equal(previous, current) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/// Hook into disputes handling.
|
/// Hook into disputes handling.
|
||||||
///
|
///
|
||||||
/// Allows decoupling parachains handling from disputes so that it can
|
/// Allows decoupling parachains handling from disputes so that it can
|
||||||
@@ -163,7 +192,10 @@ pub trait DisputesHandler<BlockNumber: Ord> {
|
|||||||
) {
|
) {
|
||||||
return Err(())
|
return Err(())
|
||||||
}
|
}
|
||||||
if statement_sets.as_slice().windows(2).any(|sub| sub.get(0) == sub.get(1)) {
|
// Sorted, so according to session and candidate hash, this will detect duplicates.
|
||||||
|
if contains_duplicates_in_sorted_iter(statement_sets, |previous, current| {
|
||||||
|
current.session == previous.session && current.candidate_hash == previous.candidate_hash
|
||||||
|
}) {
|
||||||
return Err(())
|
return Err(())
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1360,6 +1392,16 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_contains_duplicates_in_sorted_iter() {
|
||||||
|
// We here use the implicit ascending sorting and builtin equality of integers
|
||||||
|
let v = vec![1, 2, 3, 5, 5, 8];
|
||||||
|
assert_eq!(true, contains_duplicates_in_sorted_iter(&v, |a, b| a == b));
|
||||||
|
|
||||||
|
let v = vec![1, 2, 3, 4];
|
||||||
|
assert_eq!(false, contains_duplicates_in_sorted_iter(&v, |a, b| a == b));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dispute_state_flag_from_state() {
|
fn test_dispute_state_flag_from_state() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -2966,24 +3008,20 @@ mod tests {
|
|||||||
<Test as frame_system::Config>::BlockNumber,
|
<Test as frame_system::Config>::BlockNumber,
|
||||||
>>::deduplicate_and_sort_dispute_data(&mut disputes).unwrap_err();
|
>>::deduplicate_and_sort_dispute_data(&mut disputes).unwrap_err();
|
||||||
|
|
||||||
use core::cmp::Ordering;
|
// assert ordering of local only disputes, and at the same time, and being free of duplicates
|
||||||
|
|
||||||
assert_eq!(disputes_orig.len(), disputes.len() + 1);
|
assert_eq!(disputes_orig.len(), disputes.len() + 1);
|
||||||
|
|
||||||
// assert ordering of local only disputes
|
let are_these_equal = |a: &DisputeStatementSet, b: &DisputeStatementSet| {
|
||||||
disputes.windows(2).for_each(|window| {
|
use core::cmp::Ordering;
|
||||||
let a = window[0].clone();
|
|
||||||
let b = window[1].clone();
|
|
||||||
// we only have local disputes here, so sorting of those adheres to the
|
// we only have local disputes here, so sorting of those adheres to the
|
||||||
// simplified sorting logic
|
// simplified sorting logic
|
||||||
let session_cmp = a.session.cmp(&b.session);
|
let cmp =
|
||||||
let cmp = if session_cmp == Ordering::Equal {
|
a.session.cmp(&b.session).then_with(|| a.candidate_hash.cmp(&b.candidate_hash));
|
||||||
b.candidate_hash.cmp(&b.candidate_hash)
|
|
||||||
} else {
|
|
||||||
session_cmp
|
|
||||||
};
|
|
||||||
assert_ne!(cmp, Ordering::Greater);
|
assert_ne!(cmp, Ordering::Greater);
|
||||||
});
|
cmp == Ordering::Equal
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(false, contains_duplicates_in_sorted_iter(&disputes, are_these_equal));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3490,72 +3528,187 @@ mod tests {
|
|||||||
let sig_a = v0.sign(&payload);
|
let sig_a = v0.sign(&payload);
|
||||||
let sig_a_against = v1.sign(&payload_against);
|
let sig_a_against = v1.sign(&payload_against);
|
||||||
|
|
||||||
let sig_b = v0.sign(&payload);
|
let statements = vec![
|
||||||
let sig_b_against = v1.sign(&payload_against);
|
(
|
||||||
|
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
||||||
|
ValidatorIndex(0),
|
||||||
|
sig_a.clone(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
||||||
|
ValidatorIndex(1),
|
||||||
|
sig_a_against.clone(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
let mut statements = vec![
|
let mut sets = vec![
|
||||||
DisputeStatementSet {
|
DisputeStatementSet {
|
||||||
candidate_hash: candidate_hash_a.clone(),
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
session: 1,
|
session: 1,
|
||||||
statements: vec![
|
statements: statements.clone(),
|
||||||
(
|
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(0),
|
|
||||||
sig_a.clone(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(1),
|
|
||||||
sig_a_against.clone(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
DisputeStatementSet {
|
DisputeStatementSet {
|
||||||
candidate_hash: candidate_hash_a.clone(),
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
session: 1,
|
session: 1,
|
||||||
statements: vec![
|
statements: statements.clone(),
|
||||||
(
|
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(0),
|
|
||||||
sig_b.clone(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(1),
|
|
||||||
sig_b_against.clone(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// `Err(())` indicates presence of duplicates
|
// `Err(())` indicates presence of duplicates
|
||||||
assert!(<Pallet::<Test> as DisputesHandler<
|
assert!(<Pallet::<Test> as DisputesHandler<
|
||||||
<Test as frame_system::Config>::BlockNumber,
|
<Test as frame_system::Config>::BlockNumber,
|
||||||
>>::deduplicate_and_sort_dispute_data(&mut statements)
|
>>::deduplicate_and_sort_dispute_data(&mut sets)
|
||||||
.is_err());
|
.is_err());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
statements,
|
sets,
|
||||||
vec![DisputeStatementSet {
|
vec![DisputeStatementSet {
|
||||||
candidate_hash: candidate_hash_a.clone(),
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
session: 1,
|
session: 1,
|
||||||
statements: vec![
|
statements,
|
||||||
(
|
|
||||||
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(0),
|
|
||||||
sig_a.clone(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
|
||||||
ValidatorIndex(1),
|
|
||||||
sig_a_against.clone(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assure_no_duplicate_statements_sets_are_fine() {
|
||||||
|
new_test_ext(Default::default()).execute_with(|| {
|
||||||
|
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
|
let v1 = <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())],
|
||||||
|
Some(vec![(&0, v0.public()), (&1, v1.public())]),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
|
let candidate_hash_a = CandidateHash(sp_core::H256::repeat_byte(1));
|
||||||
|
|
||||||
|
let payload = ExplicitDisputeStatement {
|
||||||
|
valid: true,
|
||||||
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
|
session: 1,
|
||||||
|
}
|
||||||
|
.signing_payload();
|
||||||
|
|
||||||
|
let payload_against = ExplicitDisputeStatement {
|
||||||
|
valid: false,
|
||||||
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
|
session: 1,
|
||||||
|
}
|
||||||
|
.signing_payload();
|
||||||
|
|
||||||
|
let sig_a = v0.sign(&payload);
|
||||||
|
let sig_a_against = v1.sign(&payload_against);
|
||||||
|
|
||||||
|
let statements = vec![
|
||||||
|
(
|
||||||
|
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
||||||
|
ValidatorIndex(0),
|
||||||
|
sig_a.clone(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
||||||
|
ValidatorIndex(1),
|
||||||
|
sig_a_against.clone(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
let sets = vec![
|
||||||
|
DisputeStatementSet {
|
||||||
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
|
session: 1,
|
||||||
|
statements: statements.clone(),
|
||||||
|
},
|
||||||
|
DisputeStatementSet {
|
||||||
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
|
session: 2,
|
||||||
|
statements: statements.clone(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// `Err(())` indicates presence of duplicates
|
||||||
|
assert!(<Pallet::<Test> as DisputesHandler<
|
||||||
|
<Test as frame_system::Config>::BlockNumber,
|
||||||
|
>>::assure_deduplicated_and_sorted(&sets)
|
||||||
|
.is_ok());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assure_detects_duplicate_statements_sets() {
|
||||||
|
new_test_ext(Default::default()).execute_with(|| {
|
||||||
|
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
|
||||||
|
let v1 = <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())],
|
||||||
|
Some(vec![(&0, v0.public()), (&1, v1.public())]),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
|
let candidate_hash_a = CandidateHash(sp_core::H256::repeat_byte(1));
|
||||||
|
|
||||||
|
let payload = ExplicitDisputeStatement {
|
||||||
|
valid: true,
|
||||||
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
|
session: 1,
|
||||||
|
}
|
||||||
|
.signing_payload();
|
||||||
|
|
||||||
|
let payload_against = ExplicitDisputeStatement {
|
||||||
|
valid: false,
|
||||||
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
|
session: 1,
|
||||||
|
}
|
||||||
|
.signing_payload();
|
||||||
|
|
||||||
|
let sig_a = v0.sign(&payload);
|
||||||
|
let sig_a_against = v1.sign(&payload_against);
|
||||||
|
|
||||||
|
let statements = vec![
|
||||||
|
(
|
||||||
|
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
|
||||||
|
ValidatorIndex(0),
|
||||||
|
sig_a.clone(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
|
||||||
|
ValidatorIndex(1),
|
||||||
|
sig_a_against.clone(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
let sets = vec![
|
||||||
|
DisputeStatementSet {
|
||||||
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
|
session: 1,
|
||||||
|
statements: statements.clone(),
|
||||||
|
},
|
||||||
|
DisputeStatementSet {
|
||||||
|
candidate_hash: candidate_hash_a.clone(),
|
||||||
|
session: 1,
|
||||||
|
statements: statements.clone(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// `Err(())` indicates presence of duplicates
|
||||||
|
assert!(<Pallet::<Test> as DisputesHandler<
|
||||||
|
<Test as frame_system::Config>::BlockNumber,
|
||||||
|
>>::assure_deduplicated_and_sorted(&sets)
|
||||||
|
.is_err());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn filter_ignores_single_sided() {
|
fn filter_ignores_single_sided() {
|
||||||
new_test_ext(Default::default()).execute_with(|| {
|
new_test_ext(Default::default()).execute_with(|| {
|
||||||
|
|||||||
Reference in New Issue
Block a user