use crate::ext::{IDNEnvironment, RandomReadErr};
use alloc::vec::Vec;
use ink::EnvAccess;
use rand::{seq::IteratorRandom, SeedableRng};
use rand_chacha::ChaCha12Rng;
pub fn select<T: Clone>(
env: EnvAccess<IDNEnvironment>,
list: Vec<T>,
ctx: [u8; 32],
n: usize,
) -> Result<Vec<T>, RandomReadErr> {
let seed = env.extension().fetch_random(ctx)?;
let mut rng = ChaCha12Rng::from_seed(seed);
Ok(list.into_iter().choose_multiple(&mut rng, n))
}
#[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
}
}
#[test]
fn test_select_empty_zero_elems() {
test::register_chain_extension(MockRandExtension { seed: [0x42; 32], should_fail: false });
let ctx = [0u8; 32];
let data: Vec<u8> = vec![];
let env = EnvAccess::<IDNEnvironment>::default();
let result = select(env, data, ctx, 0);
assert!(result.is_ok());
let items = result.unwrap();
assert!(items.is_empty());
}
#[test]
fn test_select_empty_1_elems_selects_nothing() {
test::register_chain_extension(MockRandExtension { seed: [0x42; 32], should_fail: false });
let ctx = [0u8; 32];
let data: Vec<u8> = vec![];
let env = EnvAccess::<IDNEnvironment>::default();
let result = select(env, data, ctx, 1);
assert!(result.is_ok());
let items = result.unwrap();
assert!(items.is_empty());
}
#[test]
fn test_select_nonempty_zero_elems() {
test::register_chain_extension(MockRandExtension { seed: [0x42; 32], should_fail: false });
let ctx = [0u8; 32];
let data: Vec<u8> = vec![1, 2, 3];
let env = EnvAccess::<IDNEnvironment>::default();
let result = select(env, data, ctx, 0);
assert!(result.is_ok());
let items = result.unwrap();
assert!(items.is_empty());
}
#[test]
fn test_select_nonempty_one_elem_gets_one() {
test::register_chain_extension(MockRandExtension { seed: [0x42; 32], should_fail: false });
let ctx = [0u8; 32];
let data: Vec<u8> = vec![1, 2, 3];
let env = EnvAccess::<IDNEnvironment>::default();
let result = select(env, data.clone(), ctx, 1);
assert!(result.is_ok());
let items = result.unwrap();
assert!(items.len() == 1);
assert!(data.contains(&items[0]));
}
#[test]
fn test_select_nonempty_n_elem_gets_n() {
test::register_chain_extension(MockRandExtension { seed: [0x42; 32], should_fail: false });
let ctx = [0u8; 32];
let data: Vec<u8> = vec![1, 2, 3];
let env = EnvAccess::<IDNEnvironment>::default();
let result = select(env, data.clone(), ctx, 2);
assert!(result.is_ok());
let items = result.unwrap();
assert!(items.len() == 2);
for item in &items {
assert!(data.contains(item));
}
}
#[test]
fn test_select_oversize_does_not_exceed_max_size() {
test::register_chain_extension(MockRandExtension { seed: [0x42; 32], should_fail: false });
let ctx = [0u8; 32];
let data: Vec<u8> = vec![1, 2, 3];
let env = EnvAccess::<IDNEnvironment>::default();
let result = select(env, data.clone(), ctx, 2);
assert!(result.is_ok());
let items = result.unwrap();
assert!(items.len() == 2);
for item in &items {
assert!(data.contains(item));
}
}
}