use crate::ext::{IDNEnvironment, RandomReadErr};
use ink::EnvAccess;
use rand::{seq::SliceRandom, SeedableRng};
use rand_chacha::ChaCha12Rng;
pub fn shuffle<T>(
env: EnvAccess<IDNEnvironment>,
list: &mut [T],
ctx: [u8; 32],
) -> Result<(), RandomReadErr>
where
T: alloc::fmt::Debug,
{
let seed = env.extension().fetch_random(ctx)?;
let mut rng = ChaCha12Rng::from_seed(seed);
list.shuffle(&mut rng);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use codec::Encode;
use ink::{env::test, EnvAccess};
pub struct MockRandExtension {
pub seed: [u8; 32],
pub should_fail: bool,
}
impl test::ChainExtension for MockRandExtension {
fn ext_id(&self) -> u16 {
42
}
fn call(&mut self, _func_id: u16, _input: &[u8], output: &mut Vec<u8>) -> u32 {
if self.should_fail {
return 1;
}
output.extend(self.seed.encode());
0
}
}
#[ink::test]
fn test_shuffle_empty() {
test::register_chain_extension(MockRandExtension { seed: [0x42; 32], should_fail: false });
let ctx = [0u8; 32];
let mut data: Vec<u8> = vec![];
let env = EnvAccess::<IDNEnvironment>::default();
let result = shuffle(env, &mut data, ctx);
assert!(result.is_ok());
assert!(data.is_empty());
}
#[ink::test]
fn test_shuffle_nonempty_slice() {
test::register_chain_extension(MockRandExtension { seed: [0x42; 32], should_fail: false });
let ctx = [0u8; 32];
let mut data: Vec<u8> = (0..10).collect();
let env = EnvAccess::<IDNEnvironment>::default();
let result = shuffle(env, &mut data, ctx);
assert!(result.is_ok());
let mut sorted = data.clone();
sorted.sort();
assert_eq!(sorted, (0..10).collect::<Vec<_>>());
}
#[ink::test]
fn test_shuffle_different_seeds_produce_different_permutations() {
let ctx = [0u8; 32];
let mut data1: Vec<u8> = (0..10).collect();
let mut data2: Vec<u8> = (0..10).collect();
test::register_chain_extension(MockRandExtension { seed: [0x01; 32], should_fail: false });
let env1 = EnvAccess::<IDNEnvironment>::default();
shuffle(env1, &mut data1, ctx).unwrap();
test::register_chain_extension(MockRandExtension { seed: [0x02; 32], should_fail: false });
let env2 = EnvAccess::<IDNEnvironment>::default();
shuffle(env2, &mut data2, ctx).unwrap();
assert_ne!(data1, data2, "different seeds should produce different shuffles");
let mut sorted1 = data1.clone();
sorted1.sort();
let mut sorted2 = data2.clone();
sorted2.sort();
assert_eq!(sorted1, (0..10).collect::<Vec<_>>());
assert_eq!(sorted2, (0..10).collect::<Vec<_>>());
}
#[ink::test]
fn test_shuffle_error() {
test::register_chain_extension(MockRandExtension { seed: [0x42; 32], should_fail: true });
let ctx = [0u8; 32];
let mut data: Vec<u8> = (0..5).collect();
let env = EnvAccess::<IDNEnvironment>::default();
let result = shuffle(env, &mut data, ctx);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), RandomReadErr::FailGetRandomSource);
}
}