Remove dependency on rand's SliceRandom shuffle implementation in gossip-support (#2555)

In gossip-support, we shuffled the list of authorities using the
`rand::seq::SliceRandom::shuffle`. This function's behavior is
unspecified beyond being `O(n)` and could change in the future, leading
to network issues between nodes using different shuffling algorithms. In
practice, the implementation was a Fisher-Yates shuffle. This PR
replaces the call with a re-implementation of Fisher-Yates and adds a
test to ensure the behavior is the same between the two at the moment.
This commit is contained in:
asynchronous rob
2023-11-30 11:55:33 -06:00
committed by GitHub
parent 6742aba05f
commit 1f2ccaea05
4 changed files with 48 additions and 2 deletions
@@ -22,6 +22,8 @@ use assert_matches::assert_matches;
use async_trait::async_trait;
use futures::{executor, future, Future};
use lazy_static::lazy_static;
use quickcheck::quickcheck;
use rand::seq::SliceRandom as _;
use sc_network::multiaddr::Protocol;
use sp_authority_discovery::AuthorityPair as AuthorityDiscoveryPair;
@@ -710,3 +712,23 @@ fn issues_a_connection_request_when_last_request_was_mostly_unresolved() {
assert_eq!(state.last_session_index, Some(1));
assert!(state.last_failure.is_none());
}
// note: this test was added at a time where the default `rand::SliceRandom::shuffle`
// function was used to shuffle authorities for the topology and ensures backwards compatibility.
//
// in the same commit, an explicit fisher-yates implementation was added in place of the unspecified
// behavior of that function. If this test begins to fail at some point in the future, it can simply
// be removed as the desired behavior has been preserved.
quickcheck! {
fn rng_shuffle_equals_fisher_yates(x: Vec<i32>, seed_base: u8) -> bool {
let mut rng1: ChaCha20Rng = SeedableRng::from_seed([seed_base; 32]);
let mut rng2: ChaCha20Rng = SeedableRng::from_seed([seed_base; 32]);
let mut data1 = x.clone();
let mut data2 = x;
data1.shuffle(&mut rng1);
crate::fisher_yates_shuffle(&mut rng2, &mut data2[..]);
data1 == data2
}
}