如何在Substrate的no_std环境下生成指定范围随机数
Substrate no_std环境下指定范围随机数实现方案
你已经通过RandomnessSource拿到了u64类型的基础随机数,只需要通过无偏差的范围映射处理即可实现指定范围的随机数生成,无需依赖rand库。
核心实现逻辑
不要直接使用random_number % range的简单模运算,会存在统计偏差:u64的最大值不是所有区间长度的整数倍,落在余数区间的数值会被多采样,严谨性不足。我们可以通过阈值校验剔除偏差部分:
/// 生成[0, range)范围内的无偏差u64随机数 fn get_bounded_random(random_raw: u64, range: u64) -> u64 { // 计算u64范围内可以被range整除的最大阈值 let threshold = u64::MAX - (u64::MAX % range); let mut r = random_raw; // 剔除超出阈值的随机数避免偏差 while r >= threshold { // 这里可以替换为用新nonce重新调用RandomnessSource取随机数 r = sp_core::hashing::blake2_128(&r.encode())[0..8] .try_into() .map(u64::from_be_bytes) .unwrap(); } r % range }
如果需要生成[min, max)区间的随机数,只需要额外加偏移量即可:
let result = min + get_bounded_random(random_raw, max - min);
整合到现有业务代码的示例
fn draw_juror_for_citizen_profile_function( citizen_id: u128, length: usize, ) -> DispatchResult { // 边界校验:区间长度不能为0 ensure!(length > 0, Error::<T>::InvalidJurorListLength); let nonce = Self::get_and_increment_nonce(); let random_seed = T::RandomnessSource::random(&nonce).encode(); let random_number = u64::decode(&mut random_seed.as_ref()) .expect("secure hashes should always be bigger than u32; qed"); // 生成0到length范围内的随机陪审员索引 let juror_index = get_bounded_random(random_number, length as u64) as usize; // 后续业务逻辑 // ... Ok(()) }
注意事项
- 如果业务对随机偏差容忍度很高,可以简化逻辑直接用
random_number % range as u64,不需要阈值校验 - RandomnessSource的随机安全性由你选择的实现决定,敏感业务建议结合自定义nonce、用户输入等多因子混合打散随机种子,避免随机数被预测
- 超出阈值时的随机数刷新逻辑,你也可以替换为用
nonce + 1重新调用RandomnessSource获取新种子
内容的提问来源于stack exchange,提问作者Amiya Behera
相关产品推荐
相关产品推荐

