这篇文章介绍如何在Substrate Runtime生成(不安全的)随机性。 随机性在计算机程序中非常有用,区块链中的以下应用场景就经常用到随机性:菠菜游戏、CryptoKitties生成小猫的DNA、选择区块生产者等。 但要在确定性的计算机中生成真正的随机性是很难的,因此通过计算机生成的随机数本质上是一种伪随机数(pseudo random number),只是尽可能地“随机”。 目前已经开发出一些技术来解决这个问题,比如RanDAO和VRF(Verifiable Random Functions,可验证随机函数)。 Substrate使用frame_support::traits::Randomness抽象了随机性源的实现,并提供了一些实现,下面的代码演示如何使用Randomness特征和其实现。 编写模块代码 修改pallets/template/src/lib.rs的代码如下: #![cfg_attr(not(feature = "std"), no_std)] use frame_support::{ decl_event, decl_module, decl_storage, dispatch::DispatchResult, traits::Randomness, }; use frame_system::{self as system, ensure_signed}; use codec::Encode; use sp_core::H256; use sp_std::vec::Vec; pub trait Trait: system::Trait { type Event: From<Event> + Into<<Self as system::Trait>::Event>; type RandomnessSource: Randomness<H256>; } decl_storage! { trait Store for Module<T: Trait> as RandomnessPallet { Nonce get(fn nonce): u32; } } decl_event!( pub enum Event { RandomnessConsumed(H256, H256), } ); decl_module! { pub struct Module<T: Trait> for enum Call where origin: T::Origin { fn deposit_event() = default; #[weight = 10_000] fn consume_randomness(origin) -> DispatchResult { let _ = ensure_signed(origin)?; let subject = Self::encode_and_update_nonce(); let random_seed = T::RandomnessSource::random_seed(); let random_result = T::RandomnessSource::random(&subject); Self::deposit_event(Event::RandomnessConsumed(random_seed, random_result)); Ok(()) } } } impl<T: Trait> Module<T> { fn encode_and_update_nonce() -> Vec<u8> { let nonce = Nonce::get(); Nonce::put(nonce.wrapping_add(1)); nonce.encode() } } 下面详细分析模块代码。 配置Cargo.toml 此模块使用了sp-core、sp_runtime、sp-std模块,需要在Cargo.toml中进行配置。 [dependencies]下添加: sp-core = { version = '2.0.0', default-features = false } sp-runtime = { version = '2.0.0', default-features = false } sp-std = { version = '2.0.0', default-features = false } [features]的std数组下添加: 'sp-core/std', 'sp-runtime/std', 配置Runtime 下面配置Runtime,修改runtime/src/lib.rs的代码。 修改第265行代码: impl pallet_template::Trait for Runtime { type Event = Event; } 改为: impl pallet_template::Trait for Runtime { type Event = Event; type RandomnessSource = CollectiveFlip; } pub type CollectiveFlip = pallet_randomness_collective_flip::Module<Runtime>; 模块代码中pub trait Trait: system::Trait{...}中声明的类型,需要在此处定义。 修改第285行代码: TemplateModule: pallet_template::{Module, Call, Storage, Event<T>}, 改为: TemplateModule: pallet_template::{Module, Call, Storage, Event}, 此模块只使用基本事件,需要去掉泛型<T>。 Randomness Trait Randomness特征提供了两个方法: fn random_seed() -> Outputfn random(subject: &[u8]) -> Output这两个方法均提供了traits类型参数中指定的类型的伪随机值。 random_seed方法不带任何参数,返回一个随机种子,该种子在每个区块更改一次,如果在同一个区块中调用此方法两次会得到相同的结果。 random方法采用字节数组&[u8]类型作为参数,与随机种子一起使用以计算最终的随机值,这种方式可以在同一个区块中获得不同的随机值。 &[u8]类型的参数称为主题(subject),用于主题的常见值包括: 区块编号;调用者的AccountId;一个nonce;一个模块指定的标识符;一个包含以上几种类型的元组;声明随机性源 pub trait Trait: system::Trait { type Event: From<Event> + Into<<Self as system::Trait>::Event>; type RandomnessSource: Randomness<H256>; } 声明随机性源类型RandomnessSource,指定输出类型为Struct sp_core::H256。 调用随机性源 fn consume_randomness(origin) -> DispatchResult { let _ = ensure_signed(origin)?; let subject = Self::encode_and_update_nonce(); let random_seed = T::RandomnessSource::random_seed(); let random_result = T::RandomnessSource::random(&subject); Self::deposit_event(Event::RandomnessConsumed(random_seed, random_result)); Ok(()) } 这里主题参数是由encode_and_update_nonce()函数提供的,它的定义如下: fn encode_and_update_nonce() -> Vec<u8> { let nonce = Nonce::get(); Nonce::put(nonce.wrapping_add(1)); nonce.encode() } 只是简单的加1。 采用哪种随机性源算法实现在runtime/src/lib.rs中定义,默认使用的是randomness_collective_flip模块提供的算法。 也可以使用babe模块提供的VRF随机性源算法,相应的业务逻辑代码该改成: let random_seed = T::BabeRandomnessSource::random_seed(); let random_result = T::BabeRandomnessSource::random(&subject); 在生产环境中,一般使用Babe VRF而不是Collective Flip。 编译 编译命令如下: cd substrate-node-template cargo +nightly-2020-08-23 build --release 使用了nightly-2020-08-23这个较稳定的cargo nightly版本以避免编译过程中出现bug。 测试 启动node-template ./target/release/node-template --dev 打开https://polkadot.js.org/apps,切换网络为DEVELOPMENT-Local Node: 选项卡选择开发者-交易,“提交下面的外部信息”选择templateModule,会自动获取到定义的函数consumeRandomness(): 这里选择BOB账户作为consumeRandomness()函数的调用者。 点击右下角“提交交易”按钮,点击“签名并提交”: 就会调用decl_module!中定义的consume_randomness函数,并触发事件RandomnessConsumed。 选项卡选择网络-浏览-链信息,在右侧可以看到最新触发的事件: 可以看到触发了模块代码中定义的templateModule.RandomnessConsumed事件,这里触发了两次,每次广播的数据分别是random_seed和random_result。 选项卡选择开发者-链状态-存储,“查询所选状态”选择templateModule,查看nonce(): u32中数据: 可以查看目前生成随机数的次数。 —- 编译者/作者:松果 玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。 |
【Substrate开发教程】23 - 如何在基于Substrate的区块链上生成随机数?
2020-11-07 松果 来源:区块链网络
LOADING...
相关阅读:
- 高师谈币:11.7比特币承压16000多头趋势能否延续2020-11-07
- KKR·DEX四币连环挖矿怎么样KKR·DEX项目简介2020-11-07
- 多头趋势未能改变谨防高位多头抛压2020-11-07
- 宣布的四款NFT驱动的游戏中的“ Formula E”赛车冠军2020-11-07
- UBI.city: 我们为什么会选择基于CirclesUBI?2020-11-07