整合随机性
Please read Substrate to Polkadot SDK page first.
随机性在计算机程序中用于许多应用程序。例如,游戏应用程序、NFT 创建和选择区块作者都需要一定程度的随机性。
在确定性计算机中很难获得真正的随机性。 在区块链的上下文中尤其如此,当网络中的所有节点都必须就链的状态达成一致时。 FRAME 为运行时工程师提供了一个 链上随机性 源,使用 Randomness 特性。
本指南解释了如何通过使用 random
方法和 nonce 作为主题来使用 FRAME 的 Randomness 特性。
本指南还说明了如何通过将 RandomCollectiveFlip
模块分配给公开“random”类型的模块的配置特性来增加随机值的熵。
导入 Randomness
-
在您想要使用的模块中,从
frame_support
导入Randomness
特性:use frame_support::traits::Randomness;
-
将其包含在模块的配置特性中:
#[pallet::config] pub trait frame_system::Config { type MyRandomness: Randomness<Self::Hash, BlockNumberFor<Self>>; }
请注意,
Randomness
特性指定了Output
和BlockNumber
类型的泛型返回。 在您的模块中使用frame_system
中的BlockNumber
和Hash
来满足该特性的要求。正如 此特性的文档 中所述,充其量,此特性可以为您提供很久以前难以预测但最近已变得易于预测的随机性。 在评估您对它的使用时,请记住这一点。
创建 nonce 并将其用于您的随机性实现
使用 nonce 作为 frame_support::traits::Randomness::random(subject: &[u8])
方法的主题。
-
在模块中包含 nonce 分为两个步骤:
- 创建
Nonce
存储项。 存储项可以是u32
或u64
类型。 - 创建私有 nonce 函数。 此函数在每次使用时都会递增 nonce。
increment_nonce()
私有函数可以这样实现,它既返回又更新 nonce。 例如:fn get_and_increment_nonce() -> Vec<u8> { let nonce = Nonce::<T>::get(); Nonce::<T>::put(nonce.wrapping_add(1)); nonce.encode() }
要了解有关 wrapping 和 encoding 方法的更多信息,请参阅 Rust 文档中的
wrapping_add
和encode
。 - 创建
-
在可调度函数中使用 Randomness。
使用 nonce,您可以调用
Randomness
公开的random()
方法。 下面的代码片段是一个模拟示例,假设已实现相关的事件和存储项:#[pallet::weight(100)] pub fn create_unique( origin: OriginFor<T>) -> DispatchResultWithPostInfo { // 调用此可调度函数的帐户。 let sender = ensure_signed(origin)?; // 随机值。 let nonce = Self::get_and_increment_nonce(); let (randomValue, _) = T::MyRandomness::random(&nonce); // 将随机值写入存储。 <MyStorageItem<T>>::put(randomValue); Self::deposit_event(Event::UniqueCreated(randomValue)); }
-
更新模块的运行时实现。
因为您已向模块的配置特性添加了一个类型,所以
Config
为进一步增强Randomness
特性派生的随机性提供了机会。 这是通过使用 Randomness Collective Flip 模块 来实现的。将此模块与
Randomness
特性一起使用将大大提高random()
处理的熵。在
runtime/src/lib.rs
中,假设pallet_random_collective_flip
在construct_runtime
中被实例化为RandomCollectiveFlip
,请按以下方式指定公开的类型:impl my_pallet::Config for Runtime{ type Event; type MyRandomness = RandomCollectiveFlip; }