mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 21:41:12 +00:00
more clear randomness API for BABE (#8180)
* more clear randomness API for BABE * babe: move randomness utilities to its own file * node: use babe::RandomnessFromOneEpochAgo in random_seed implementation * frame-support: annotate randomness trait with block number * pallet-randomness-collective-flip: fix for new randomness trait * pallet-society: fix randomness usage * pallet-lottery: fix randomness usage * pallet-contracts: fix randomness usage * pallet-babe: fix randomness usage we need to track when the current and previous epoch started so that we know the block number by each existing on-chain was known * node: fix random_seed * node-template: fix random_seed * frame-support: extend docs * babe: add test for epoch starting block number tracking * babe: fix epoch randomness docs * frame: add todos for dealing with randomness api changes Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
committed by
GitHub
parent
f3d4355a20
commit
e2960c383e
Generated
+3
-1
@@ -1814,7 +1814,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "frame-support-test"
|
||||
version = "2.0.1"
|
||||
version = "3.0.0"
|
||||
dependencies = [
|
||||
"frame-metadata",
|
||||
"frame-support",
|
||||
@@ -4983,6 +4983,7 @@ version = "3.0.0"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
"frame-support-test",
|
||||
"frame-system",
|
||||
"pallet-balances",
|
||||
"parity-scale-codec",
|
||||
@@ -5278,6 +5279,7 @@ name = "pallet-society"
|
||||
version = "3.0.0"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-support-test",
|
||||
"frame-system",
|
||||
"pallet-balances",
|
||||
"parity-scale-codec",
|
||||
|
||||
@@ -358,7 +358,7 @@ impl_runtime_apis! {
|
||||
}
|
||||
|
||||
fn random_seed() -> <Block as BlockT>::Hash {
|
||||
RandomnessCollectiveFlip::random_seed()
|
||||
RandomnessCollectiveFlip::random_seed().0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1220,7 +1220,7 @@ impl_runtime_apis! {
|
||||
}
|
||||
|
||||
fn random_seed() -> <Block as BlockT>::Hash {
|
||||
RandomnessCollectiveFlip::random_seed()
|
||||
pallet_babe::RandomnessFromOneEpochAgo::<Runtime>::random_seed().0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ use codec::{Decode, Encode};
|
||||
use frame_support::{
|
||||
decl_error, decl_module, decl_storage,
|
||||
dispatch::DispatchResultWithPostInfo,
|
||||
traits::{FindAuthor, Get, KeyOwnerProofSystem, OneSessionHandler, Randomness as RandomnessT},
|
||||
traits::{FindAuthor, Get, KeyOwnerProofSystem, OneSessionHandler},
|
||||
weights::{Pays, Weight},
|
||||
Parameter,
|
||||
};
|
||||
@@ -33,7 +33,7 @@ use frame_system::{ensure_none, ensure_root, ensure_signed};
|
||||
use sp_application_crypto::Public;
|
||||
use sp_runtime::{
|
||||
generic::DigestItem,
|
||||
traits::{Hash, IsMember, One, SaturatedConversion, Saturating, Zero},
|
||||
traits::{IsMember, One, SaturatedConversion, Saturating, Zero},
|
||||
ConsensusEngineId, KeyTypeId,
|
||||
};
|
||||
use sp_session::{GetSessionNumber, GetValidatorCount};
|
||||
@@ -49,8 +49,9 @@ use sp_consensus_vrf::schnorrkel;
|
||||
|
||||
pub use sp_consensus_babe::{AuthorityId, PUBLIC_KEY_LENGTH, RANDOMNESS_LENGTH, VRF_OUTPUT_LENGTH};
|
||||
|
||||
mod equivocation;
|
||||
mod default_weights;
|
||||
mod equivocation;
|
||||
mod randomness;
|
||||
|
||||
#[cfg(any(feature = "runtime-benchmarks", test))]
|
||||
mod benchmarking;
|
||||
@@ -60,6 +61,9 @@ mod mock;
|
||||
mod tests;
|
||||
|
||||
pub use equivocation::{BabeEquivocationOffence, EquivocationHandler, HandleEquivocation};
|
||||
pub use randomness::{
|
||||
CurrentBlockRandomness, RandomnessFromOneEpochAgo, RandomnessFromTwoEpochsAgo,
|
||||
};
|
||||
|
||||
pub trait Config: pallet_timestamp::Config {
|
||||
/// The amount of time, in slots, that each epoch should last.
|
||||
@@ -220,6 +224,13 @@ decl_storage! {
|
||||
/// secondary plain slots are enabled (which don't contain a VRF output).
|
||||
AuthorVrfRandomness get(fn author_vrf_randomness): MaybeRandomness;
|
||||
|
||||
/// The block numbers when the last and current epoch have started, respectively `N-1` and
|
||||
/// `N`.
|
||||
/// NOTE: We track this is in order to annotate the block number when a given pool of
|
||||
/// entropy was fixed (i.e. it was known to chain observers). Since epochs are defined in
|
||||
/// slots, which may be skipped, the block numbers may not line up with the slot numbers.
|
||||
EpochStart: (T::BlockNumber, T::BlockNumber);
|
||||
|
||||
/// How late the current block is compared to its parent.
|
||||
///
|
||||
/// This entry is populated as part of block execution and is cleaned up
|
||||
@@ -343,31 +354,6 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> RandomnessT<<T as frame_system::Config>::Hash> for Module<T> {
|
||||
/// Some BABE blocks have VRF outputs where the block producer has exactly one bit of influence,
|
||||
/// either they make the block or they do not make the block and thus someone else makes the
|
||||
/// next block. Yet, this randomness is not fresh in all BABE blocks.
|
||||
///
|
||||
/// If that is an insufficient security guarantee then two things can be used to improve this
|
||||
/// randomness:
|
||||
///
|
||||
/// - Name, in advance, the block number whose random value will be used; ensure your module
|
||||
/// retains a buffer of previous random values for its subject and then index into these in
|
||||
/// order to obviate the ability of your user to look up the parent hash and choose when to
|
||||
/// transact based upon it.
|
||||
/// - Require your user to first commit to an additional value by first posting its hash.
|
||||
/// Require them to reveal the value to determine the final result, hashing it with the
|
||||
/// output of this random function. This reduces the ability of a cabal of block producers
|
||||
/// from conspiring against individuals.
|
||||
fn random(subject: &[u8]) -> T::Hash {
|
||||
let mut subject = subject.to_vec();
|
||||
subject.reserve(VRF_OUTPUT_LENGTH);
|
||||
subject.extend_from_slice(&Self::randomness()[..]);
|
||||
|
||||
<T as frame_system::Config>::Hashing::hash(&subject[..])
|
||||
}
|
||||
}
|
||||
|
||||
/// A BABE public key
|
||||
pub type BabeKey = [u8; PUBLIC_KEY_LENGTH];
|
||||
|
||||
@@ -492,6 +478,12 @@ impl<T: Config> Module<T> {
|
||||
// Update the next epoch authorities.
|
||||
NextAuthorities::put(&next_authorities);
|
||||
|
||||
// Update the start blocks of the previous and new current epoch.
|
||||
<EpochStart<T>>::mutate(|(previous_epoch_start_block, current_epoch_start_block)| {
|
||||
*previous_epoch_start_block = sp_std::mem::take(current_epoch_start_block);
|
||||
*current_epoch_start_block = <frame_system::Module<T>>::block_number();
|
||||
});
|
||||
|
||||
// After we update the current epoch, we signal the *next* epoch change
|
||||
// so that nodes can track changes.
|
||||
let next_randomness = NextRandomness::get();
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Provides multiple implementations of the randomness trait based on the on-chain epoch
|
||||
//! randomness collected from VRF outputs.
|
||||
|
||||
use super::{
|
||||
AuthorVrfRandomness, Config, EpochStart, NextRandomness, Randomness, VRF_OUTPUT_LENGTH,
|
||||
};
|
||||
use frame_support::{traits::Randomness as RandomnessT, StorageValue};
|
||||
use sp_runtime::traits::Hash;
|
||||
|
||||
/// Randomness usable by consensus protocols that **depend** upon finality and take action
|
||||
/// based upon on-chain commitments made during the epoch before the previous epoch.
|
||||
///
|
||||
/// An off-chain consensus protocol requires randomness be finalized before usage, but one
|
||||
/// extra epoch delay beyond `RandomnessFromOneEpochAgo` suffices, under the assumption
|
||||
/// that finality never stalls for longer than one epoch.
|
||||
///
|
||||
/// All randomness is relative to commitments to any other inputs to the computation: If
|
||||
/// Alice samples randomness near perfectly using radioactive decay, but then afterwards
|
||||
/// Eve selects an arbitrary value with which to xor Alice's randomness, then Eve always
|
||||
/// wins whatever game they play.
|
||||
///
|
||||
/// All input commitments used with `RandomnessFromTwoEpochsAgo` should come from at least
|
||||
/// three epochs ago. We require BABE session keys be registered at least three epochs
|
||||
/// before being used to derive `CurrentBlockRandomness` for example.
|
||||
///
|
||||
/// All users learn `RandomnessFromTwoEpochsAgo` when epoch `current_epoch - 1` starts,
|
||||
/// although some learn it a few block earlier inside epoch `current_epoch - 2`.
|
||||
///
|
||||
/// Adversaries with enough block producers could bias this randomness by choosing upon
|
||||
/// what their block producers build at the end of epoch `current_epoch - 2` or the
|
||||
/// beginning epoch `current_epoch - 1`, or skipping slots at the end of epoch
|
||||
/// `current_epoch - 2`.
|
||||
///
|
||||
/// Adversaries should not possess many block production slots towards the beginning or
|
||||
/// end of every epoch, but they possess some influence over when they possess more slots.
|
||||
pub struct RandomnessFromTwoEpochsAgo<T>(sp_std::marker::PhantomData<T>);
|
||||
|
||||
/// Randomness usable by on-chain code that **does not depend** upon finality and takes
|
||||
/// action based upon on-chain commitments made during the previous epoch.
|
||||
///
|
||||
/// All randomness is relative to commitments to any other inputs to the computation: If
|
||||
/// Alice samples randomness near perfectly using radioactive decay, but then afterwards
|
||||
/// Eve selects an arbitrary value with which to xor Alice's randomness, then Eve always
|
||||
/// wins whatever game they play.
|
||||
///
|
||||
/// All input commitments used with `RandomnessFromOneEpochAgo` should come from at least
|
||||
/// two epochs ago, although the previous epoch might work in special cases under
|
||||
/// additional assumption.
|
||||
///
|
||||
/// All users learn `RandomnessFromOneEpochAgo` at the end of the previous epoch, although
|
||||
/// some block producers learn it several block earlier.
|
||||
///
|
||||
/// Adversaries with enough block producers could bias this randomness by choosing upon
|
||||
/// what their block producers build at either the end of the previous epoch or the
|
||||
/// beginning of the current epoch, or electing to skipping some of their own block
|
||||
/// production slots towards the end of the previous epoch.
|
||||
///
|
||||
/// Adversaries should not possess many block production slots towards the beginning or
|
||||
/// end of every epoch, but they possess some influence over when they possess more slots.
|
||||
///
|
||||
/// As an example usage, we determine parachain auctions ending times in Polkadot using
|
||||
/// `RandomnessFromOneEpochAgo` because it reduces bias from `CurrentBlockRandomness` and
|
||||
/// does not require the extra finality delay of `RandomnessFromTwoEpochsAgo`.
|
||||
pub struct RandomnessFromOneEpochAgo<T>(sp_std::marker::PhantomData<T>);
|
||||
|
||||
/// Randomness produced semi-freshly with each block, but inherits limitations of
|
||||
/// `RandomnessFromTwoEpochsAgo` from which it derives.
|
||||
///
|
||||
/// All randomness is relative to commitments to any other inputs to the computation: If
|
||||
/// Alice samples randomness near perfectly using radioactive decay, but then afterwards
|
||||
/// Eve selects an arbitrary value with which to xor Alice's randomness, then Eve always
|
||||
/// wins whatever game they play.
|
||||
///
|
||||
/// As with `RandomnessFromTwoEpochsAgo`, all input commitments combined with
|
||||
/// `CurrentBlockRandomness` should come from at least two epoch ago, except preferably
|
||||
/// not near epoch ending, and thus ideally three epochs ago.
|
||||
///
|
||||
/// Almost all users learn this randomness for a block when the block producer announces
|
||||
/// the block, which makes this randomness appear quite fresh. Yet, the block producer
|
||||
/// themselves learned this randomness at the beginning of epoch `current_epoch - 2`, at
|
||||
/// the same time as they learn `RandomnessFromTwoEpochsAgo`.
|
||||
///
|
||||
/// Aside from just biasing `RandomnessFromTwoEpochsAgo`, adversaries could also bias
|
||||
/// `CurrentBlockRandomness` by never announcing their block if doing so yields an
|
||||
/// unfavorable randomness. As such, `CurrentBlockRandomness` should be considered weaker
|
||||
/// than both other randomness sources provided by BABE, but `CurrentBlockRandomness`
|
||||
/// remains constrained by declared staking, while a randomness source like block hash is
|
||||
/// only constrained by adversaries' unknowable computational power.
|
||||
///
|
||||
/// As an example use, parachains could assign block production slots based upon the
|
||||
/// `CurrentBlockRandomness` of their relay parent or relay parent's parent, provided the
|
||||
/// parachain registers collators but avoids censorship sensitive functionality like
|
||||
/// slashing. Any parachain with slashing could operate BABE itself or perhaps better yet
|
||||
/// a BABE-like approach that derives its `CurrentBlockRandomness`, and authorizes block
|
||||
/// production, based upon the relay parent's `CurrentBlockRandomness` or more likely the
|
||||
/// relay parent's `RandomnessFromTwoEpochsAgo`.
|
||||
pub struct CurrentBlockRandomness<T>(sp_std::marker::PhantomData<T>);
|
||||
|
||||
impl<T: Config> RandomnessT<T::Hash, T::BlockNumber> for RandomnessFromTwoEpochsAgo<T> {
|
||||
fn random(subject: &[u8]) -> (T::Hash, T::BlockNumber) {
|
||||
let mut subject = subject.to_vec();
|
||||
subject.reserve(VRF_OUTPUT_LENGTH);
|
||||
subject.extend_from_slice(&Randomness::get()[..]);
|
||||
|
||||
(T::Hashing::hash(&subject[..]), EpochStart::<T>::get().0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> RandomnessT<T::Hash, T::BlockNumber> for RandomnessFromOneEpochAgo<T> {
|
||||
fn random(subject: &[u8]) -> (T::Hash, T::BlockNumber) {
|
||||
let mut subject = subject.to_vec();
|
||||
subject.reserve(VRF_OUTPUT_LENGTH);
|
||||
subject.extend_from_slice(&NextRandomness::get()[..]);
|
||||
|
||||
(T::Hashing::hash(&subject[..]), EpochStart::<T>::get().1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> RandomnessT<Option<T::Hash>, T::BlockNumber> for CurrentBlockRandomness<T> {
|
||||
fn random(subject: &[u8]) -> (Option<T::Hash>, T::BlockNumber) {
|
||||
let random = AuthorVrfRandomness::get().map(|random| {
|
||||
let mut subject = subject.to_vec();
|
||||
subject.reserve(VRF_OUTPUT_LENGTH);
|
||||
subject.extend_from_slice(&random);
|
||||
|
||||
T::Hashing::hash(&subject[..])
|
||||
});
|
||||
|
||||
(random, <frame_system::Module<T>>::block_number())
|
||||
}
|
||||
}
|
||||
@@ -349,6 +349,31 @@ fn can_fetch_current_and_next_epoch_data() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tracks_block_numbers_when_current_and_previous_epoch_started() {
|
||||
new_test_ext(5).execute_with(|| {
|
||||
// an epoch is 3 slots therefore at block 8 we should be in epoch #3
|
||||
// with the previous epochs having the following blocks:
|
||||
// epoch 1 - [1, 2, 3]
|
||||
// epoch 2 - [4, 5, 6]
|
||||
// epoch 3 - [7, 8, 9]
|
||||
progress_to_block(8);
|
||||
|
||||
let (last_epoch, current_epoch) = EpochStart::<Test>::get();
|
||||
|
||||
assert_eq!(last_epoch, 4);
|
||||
assert_eq!(current_epoch, 7);
|
||||
|
||||
// once we reach block 10 we switch to epoch #4
|
||||
progress_to_block(10);
|
||||
|
||||
let (last_epoch, current_epoch) = EpochStart::<Test>::get();
|
||||
|
||||
assert_eq!(last_epoch, 7);
|
||||
assert_eq!(current_epoch, 10);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn report_equivocation_current_session_works() {
|
||||
let (pairs, mut ext) = new_test_ext_with_pairs(3);
|
||||
|
||||
@@ -740,7 +740,9 @@ where
|
||||
}
|
||||
|
||||
fn random(&self, subject: &[u8]) -> SeedOf<T> {
|
||||
T::Randomness::random(subject)
|
||||
// TODO: change API to expose randomness freshness
|
||||
// https://github.com/paritytech/substrate/issues/8297
|
||||
T::Randomness::random(subject).0
|
||||
}
|
||||
|
||||
fn now(&self) -> &MomentOf<T> {
|
||||
|
||||
@@ -151,7 +151,7 @@ pub mod pallet {
|
||||
type Time: Time;
|
||||
|
||||
/// The generator used to supply randomness to contracts through `seal_random`.
|
||||
type Randomness: Randomness<Self::Hash>;
|
||||
type Randomness: Randomness<Self::Hash, Self::BlockNumber>;
|
||||
|
||||
/// The currency in which fees are paid and contract balances are held.
|
||||
type Currency: Currency<Self::AccountId>;
|
||||
|
||||
@@ -22,6 +22,7 @@ frame-system = { version = "3.0.0", default-features = false, path = "../system"
|
||||
frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
frame-support-test = { version = "3.0.0", path = "../support/test" }
|
||||
pallet-balances = { version = "3.0.0", path = "../balances" }
|
||||
sp-core = { version = "3.0.0", path = "../../primitives/core" }
|
||||
sp-io = { version = "3.0.0", path = "../../primitives/io" }
|
||||
|
||||
@@ -85,7 +85,7 @@ pub trait Config: frame_system::Config {
|
||||
type Currency: ReservableCurrency<Self::AccountId>;
|
||||
|
||||
/// Something that provides randomness in the runtime.
|
||||
type Randomness: Randomness<Self::Hash>;
|
||||
type Randomness: Randomness<Self::Hash, Self::BlockNumber>;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
|
||||
@@ -443,8 +443,10 @@ impl<T: Config> Module<T> {
|
||||
// Note that there is potential bias introduced by using modulus operator.
|
||||
// You should call this function with different seed values until the random
|
||||
// number lies within `u32::MAX - u32::MAX % n`.
|
||||
// TODO: deal with randomness freshness
|
||||
// https://github.com/paritytech/substrate/issues/8311
|
||||
fn generate_random_number(seed: u32) -> u32 {
|
||||
let random_seed = T::Randomness::random(&(T::ModuleId::get(), seed).encode());
|
||||
let (random_seed, _) = T::Randomness::random(&(T::ModuleId::get(), seed).encode());
|
||||
let random_number = <u32>::decode(&mut random_seed.as_ref())
|
||||
.expect("secure hashes should always be bigger than u32; qed");
|
||||
random_number
|
||||
|
||||
@@ -22,8 +22,9 @@ use crate as pallet_lottery;
|
||||
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
traits::{OnInitialize, OnFinalize, TestRandomness},
|
||||
traits::{OnFinalize, OnInitialize},
|
||||
};
|
||||
use frame_support_test::TestRandomness;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
Perbill,
|
||||
@@ -103,7 +104,7 @@ impl Config for Test {
|
||||
type ModuleId = LotteryModuleId;
|
||||
type Call = Call;
|
||||
type Currency = Balances;
|
||||
type Randomness = TestRandomness;
|
||||
type Randomness = TestRandomness<Self>;
|
||||
type Event = Event;
|
||||
type ManagerOrigin = EnsureRoot<u64>;
|
||||
type MaxCalls = MaxCalls;
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use sp_std::{prelude::*, convert::TryInto};
|
||||
use sp_runtime::traits::Hash;
|
||||
use sp_runtime::traits::{Hash, Saturating};
|
||||
use frame_support::{
|
||||
decl_module, decl_storage, traits::Randomness,
|
||||
weights::Weight
|
||||
@@ -99,7 +99,7 @@ decl_storage! {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Randomness<T::Hash> for Module<T> {
|
||||
impl<T: Config> Randomness<T::Hash, T::BlockNumber> for Module<T> {
|
||||
/// This randomness uses a low-influence function, drawing upon the block hashes from the
|
||||
/// previous 81 blocks. Its result for any given subject will be known far in advance by anyone
|
||||
/// observing the chain. Any block producer has significant influence over their block hashes
|
||||
@@ -110,14 +110,15 @@ impl<T: Config> Randomness<T::Hash> for Module<T> {
|
||||
/// WARNING: Hashing the result of this function will remove any low-influence properties it has
|
||||
/// and mean that all bits of the resulting value are entirely manipulatable by the author of
|
||||
/// the parent block, who can determine the value of `parent_hash`.
|
||||
fn random(subject: &[u8]) -> T::Hash {
|
||||
fn random(subject: &[u8]) -> (T::Hash, T::BlockNumber) {
|
||||
let block_number = <frame_system::Module<T>>::block_number();
|
||||
let index = block_number_to_index::<T>(block_number);
|
||||
|
||||
let hash_series = <RandomMaterial<T>>::get();
|
||||
if !hash_series.is_empty() {
|
||||
let seed = if !hash_series.is_empty() {
|
||||
// Always the case after block 1 is initialized.
|
||||
hash_series.iter()
|
||||
hash_series
|
||||
.iter()
|
||||
.cycle()
|
||||
.skip(index)
|
||||
.take(RANDOM_MATERIAL_LEN as usize)
|
||||
@@ -126,7 +127,12 @@ impl<T: Config> Randomness<T::Hash> for Module<T> {
|
||||
.triplet_mix()
|
||||
} else {
|
||||
T::Hash::default()
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
seed,
|
||||
block_number.saturating_sub(RANDOM_MATERIAL_LEN.into()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,8 +278,9 @@ mod tests {
|
||||
assert_eq!(CollectiveFlip::random_seed(), CollectiveFlip::random_seed());
|
||||
assert_ne!(CollectiveFlip::random(b"random_1"), CollectiveFlip::random(b"random_2"));
|
||||
|
||||
let random = CollectiveFlip::random_seed();
|
||||
let (random, known_since) = CollectiveFlip::random_seed();
|
||||
|
||||
assert_eq!(known_since, 162 - RANDOM_MATERIAL_LEN as u64);
|
||||
assert_ne!(random, H256::zero());
|
||||
assert!(!CollectiveFlip::random_material().contains(&random));
|
||||
});
|
||||
|
||||
@@ -24,6 +24,7 @@ rand_chacha = { version = "0.2", default-features = false }
|
||||
[dev-dependencies]
|
||||
sp-core = { version = "3.0.0", path = "../../primitives/core" }
|
||||
sp-io ={ version = "3.0.0", path = "../../primitives/io" }
|
||||
frame-support-test = { version = "3.0.0", path = "../support/test" }
|
||||
pallet-balances = { version = "3.0.0", path = "../balances" }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -283,7 +283,7 @@ pub trait Config<I=DefaultInstance>: system::Config {
|
||||
type Currency: ReservableCurrency<Self::AccountId>;
|
||||
|
||||
/// Something that provides randomness in the runtime.
|
||||
type Randomness: Randomness<Self::Hash>;
|
||||
type Randomness: Randomness<Self::Hash, Self::BlockNumber>;
|
||||
|
||||
/// The minimum amount of a deposit required for a bid to be made.
|
||||
type CandidateDeposit: Get<BalanceOf<Self, I>>;
|
||||
@@ -1309,7 +1309,9 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
|
||||
let mut pot = <Pot<T, I>>::get();
|
||||
|
||||
// we'll need a random seed here.
|
||||
let seed = T::Randomness::random(phrase);
|
||||
// TODO: deal with randomness freshness
|
||||
// https://github.com/paritytech/substrate/issues/8312
|
||||
let (seed, _) = T::Randomness::random(phrase);
|
||||
// seed needs to be guaranteed to be 32 bytes.
|
||||
let seed = <[u8; 32]>::decode(&mut TrailingZeroInput::new(seed.as_ref()))
|
||||
.expect("input is padded with zeroes; qed");
|
||||
@@ -1565,7 +1567,9 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
|
||||
// Start a new defender rotation
|
||||
let phrase = b"society_challenge";
|
||||
// we'll need a random seed here.
|
||||
let seed = T::Randomness::random(phrase);
|
||||
// TODO: deal with randomness freshness
|
||||
// https://github.com/paritytech/substrate/issues/8312
|
||||
let (seed, _) = T::Randomness::random(phrase);
|
||||
// seed needs to be guaranteed to be 32 bytes.
|
||||
let seed = <[u8; 32]>::decode(&mut TrailingZeroInput::new(seed.as_ref()))
|
||||
.expect("input is padded with zeroes; qed");
|
||||
|
||||
@@ -22,8 +22,9 @@ use crate as pallet_society;
|
||||
|
||||
use frame_support::{
|
||||
parameter_types, ord_parameter_types,
|
||||
traits::{OnInitialize, OnFinalize, TestRandomness},
|
||||
traits::{OnInitialize, OnFinalize},
|
||||
};
|
||||
use frame_support_test::TestRandomness;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
@@ -104,7 +105,7 @@ impl pallet_balances::Config for Test {
|
||||
impl Config for Test {
|
||||
type Event = Event;
|
||||
type Currency = pallet_balances::Module<Self>;
|
||||
type Randomness = TestRandomness;
|
||||
type Randomness = TestRandomness<Self>;
|
||||
type CandidateDeposit = CandidateDeposit;
|
||||
type WrongSideDeduction = WrongSideDeduction;
|
||||
type MaxStrikes = MaxStrikes;
|
||||
|
||||
@@ -23,13 +23,13 @@ use sp_std::{prelude::*, result, marker::PhantomData, ops::Div, fmt::Debug};
|
||||
use codec::{FullCodec, Codec, Encode, Decode, EncodeLike};
|
||||
use sp_core::u32_trait::Value as U32;
|
||||
use sp_runtime::{
|
||||
RuntimeAppPublic, RuntimeDebug, BoundToRuntimeAppPublic,
|
||||
ConsensusEngineId, DispatchResult, DispatchError,
|
||||
traits::{
|
||||
MaybeSerializeDeserialize, AtLeast32Bit, Saturating, TrailingZeroInput, Bounded, Zero,
|
||||
BadOrigin, AtLeast32BitUnsigned, Convert, UniqueSaturatedFrom, UniqueSaturatedInto,
|
||||
SaturatedConversion, StoredMapError, Block as BlockT,
|
||||
AtLeast32Bit, AtLeast32BitUnsigned, BadOrigin, Block as BlockT, Bounded, Convert,
|
||||
MaybeSerializeDeserialize, SaturatedConversion, Saturating, StoredMapError,
|
||||
UniqueSaturatedFrom, UniqueSaturatedInto, Zero,
|
||||
},
|
||||
BoundToRuntimeAppPublic, ConsensusEngineId, DispatchError, DispatchResult, RuntimeAppPublic,
|
||||
RuntimeDebug,
|
||||
};
|
||||
use sp_staking::SessionIndex;
|
||||
use crate::dispatch::Parameter;
|
||||
@@ -1413,38 +1413,42 @@ impl<T> InitializeMembers<T> for () {
|
||||
fn initialize_members(_: &[T]) {}
|
||||
}
|
||||
|
||||
// A trait that is able to provide randomness.
|
||||
pub trait Randomness<Output> {
|
||||
/// Get a "random" value
|
||||
/// A trait that is able to provide randomness.
|
||||
///
|
||||
/// Being a deterministic blockchain, real randomness is difficult to come by, different
|
||||
/// implementations of this trait will provide different security guarantees. At best,
|
||||
/// this will be randomness which was hard to predict a long time ago, but that has become
|
||||
/// easy to predict recently.
|
||||
pub trait Randomness<Output, BlockNumber> {
|
||||
/// Get the most recently determined random seed, along with the time in the past
|
||||
/// since when it was determinable by chain observers.
|
||||
///
|
||||
/// Being a deterministic blockchain, real randomness is difficult to come by. This gives you
|
||||
/// something that approximates it. At best, this will be randomness which was
|
||||
/// hard to predict a long time ago, but that has become easy to predict recently.
|
||||
/// `subject` is a context identifier and allows you to get a different result to
|
||||
/// other callers of this function; use it like `random(&b"my context"[..])`.
|
||||
///
|
||||
/// `subject` is a context identifier and allows you to get a
|
||||
/// different result to other callers of this function; use it like
|
||||
/// `random(&b"my context"[..])`.
|
||||
fn random(subject: &[u8]) -> Output;
|
||||
/// NOTE: The returned seed should only be used to distinguish commitments made before
|
||||
/// the returned block number. If the block number is too early (i.e. commitments were
|
||||
/// made afterwards), then ensure no further commitments may be made and repeatedly
|
||||
/// call this on later blocks until the block number returned is later than the latest
|
||||
/// commitment.
|
||||
fn random(subject: &[u8]) -> (Output, BlockNumber);
|
||||
|
||||
/// Get the basic random seed.
|
||||
///
|
||||
/// In general you won't want to use this, but rather `Self::random` which allows you to give a
|
||||
/// subject for the random result and whose value will be independently low-influence random
|
||||
/// from any other such seeds.
|
||||
fn random_seed() -> Output {
|
||||
/// In general you won't want to use this, but rather `Self::random` which allows
|
||||
/// you to give a subject for the random result and whose value will be
|
||||
/// independently low-influence random from any other such seeds.
|
||||
///
|
||||
/// NOTE: The returned seed should only be used to distinguish commitments made before
|
||||
/// the returned block number. If the block number is too early (i.e. commitments were
|
||||
/// made afterwards), then ensure no further commitments may be made and repeatedly
|
||||
/// call this on later blocks until the block number returned is later than the latest
|
||||
/// commitment.
|
||||
fn random_seed() -> (Output, BlockNumber) {
|
||||
Self::random(&[][..])
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides an implementation of [`Randomness`] that should only be used in tests!
|
||||
pub struct TestRandomness;
|
||||
|
||||
impl<Output: Decode + Default> Randomness<Output> for TestRandomness {
|
||||
fn random(subject: &[u8]) -> Output {
|
||||
Output::decode(&mut TrailingZeroInput::new(subject)).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to be used by block producing consensus engine modules to determine
|
||||
/// how late the current block is (e.g. in a slot-based proposal mechanism how
|
||||
/// many slots were skipped since the previous block).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "frame-support-test"
|
||||
version = "2.0.1"
|
||||
version = "3.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -53,3 +53,21 @@ impl frame_support::traits::PalletInfo for PanicPalletInfo {
|
||||
unimplemented!("PanicPalletInfo mustn't be triggered by tests");
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides an implementation of [`Randomness`] that should only be used in tests!
|
||||
pub struct TestRandomness<T>(sp_std::marker::PhantomData<T>);
|
||||
|
||||
impl<Output: codec::Decode + Default, T> frame_support::traits::Randomness<Output, T::BlockNumber>
|
||||
for TestRandomness<T>
|
||||
where
|
||||
T: frame_system::Config,
|
||||
{
|
||||
fn random(subject: &[u8]) -> (Output, T::BlockNumber) {
|
||||
use sp_runtime::traits::TrailingZeroInput;
|
||||
|
||||
(
|
||||
Output::decode(&mut TrailingZeroInput::new(subject)).unwrap_or_default(),
|
||||
frame_system::Module::<T>::block_number(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user