mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-09 15:18:00 +00:00
Clean up random seed to make a bit more flexible (#2456)
* Reformulate random seed to be more random - First 80 random values come from cycling the incomplete series ( instead of filling with zeroes) - Calculate random material each usage (use a single amalgamated ring buffer to store them for avoiding 81 lookups each time) - Mutate inputs by hashing each with: - its index (into the 81) - an additional "subject" key provided by caller This keeps the final output low-influence while still allowing it to be used as the seed to independent contexts. (Hashing the result to give the final seed is no better than using parent_hash). * Docs * Bump runtime * Update notes * Remove feature(alloc) * Update srml/system/src/lib.rs Co-Authored-By: gavofyork <github@gavwood.com>
This commit is contained in:
committed by
Sergei Pepyakin
parent
b42e37589e
commit
adba89913e
@@ -1,7 +1,6 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "strict", deny(warnings))]
|
||||
|
||||
#![feature(alloc)]
|
||||
extern crate alloc;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::slice;
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
//! Primitives for GRANDPA integration, suitable for WASM compilation.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate alloc;
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
/// Initialize a key-value collection from array.
|
||||
///
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#![cfg_attr(not(feature = "std"), feature(lang_items))]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc_error_handler))]
|
||||
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
#![cfg_attr(feature = "std", doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")]
|
||||
#![cfg_attr(not(feature = "std"), doc = "Substrate's runtime standard library as compiled without Rust's standard library.")]
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
use rstd::prelude::*;
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
#![cfg_attr(feature = "std", doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")]
|
||||
#![cfg_attr(not(feature = "std"), doc = "Substrate's runtime standard library as compiled without Rust's standard library.")]
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
//! Utility functions to interact with Substrate's Base-16 Modified Merkle Patricia tree ("trie").
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
mod error;
|
||||
mod node_header;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
|
||||
#![recursion_limit="256"]
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
use runtime_primitives::{
|
||||
generic, traits::{Verify, BlakeTwo256}, OpaqueExtrinsic, AnySignature
|
||||
|
||||
@@ -58,8 +58,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("node"),
|
||||
impl_name: create_runtime_str!("substrate-node"),
|
||||
authoring_version: 10,
|
||||
spec_version: 71,
|
||||
impl_version: 71,
|
||||
spec_version: 72,
|
||||
impl_version: 72,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
};
|
||||
|
||||
|
||||
@@ -443,7 +443,7 @@ mod tests {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("4c10fddf15e63c91ff2aa13ab3a9b7f6b19938d533829489e72ba40278a08fac").into(),
|
||||
state_root: hex!("ac2840371d51ff2e036c8fc05af7313b7a030f735c38b2f03b94cbe87bfbb7c9").into(),
|
||||
extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
//! Support code for the runtime.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitmask;
|
||||
|
||||
@@ -302,8 +302,9 @@ decl_storage! {
|
||||
pub BlockHash get(block_hash) build(|_| vec![(T::BlockNumber::zero(), hash69())]): map T::BlockNumber => T::Hash;
|
||||
/// Extrinsics data for the current block (maps an extrinsic's index to its data).
|
||||
ExtrinsicData get(extrinsic_data): map u32 => Vec<u8>;
|
||||
/// Random seed of the current block.
|
||||
RandomSeed get(random_seed) build(|_| T::Hash::default()): T::Hash;
|
||||
/// Series of block headers from the last 81 blocks that acts as random seed material. This is arranged as a
|
||||
/// ring buffer with the `i8` prefix being the index into the `Vec` of the oldest hash.
|
||||
RandomMaterial get(random_material): (i8, Vec<T::Hash>);
|
||||
/// The current block number being processed. Set by `execute_block`.
|
||||
Number get(block_number) build(|_| T::BlockNumber::sa(1u64)): T::BlockNumber;
|
||||
/// Hash of the previous block.
|
||||
@@ -395,13 +396,17 @@ impl<T: Trait> Module<T> {
|
||||
<ParentHash<T>>::put(parent_hash);
|
||||
<BlockHash<T>>::insert(*number - One::one(), parent_hash);
|
||||
<ExtrinsicsRoot<T>>::put(txs_root);
|
||||
<RandomSeed<T>>::put(Self::calculate_random());
|
||||
<RandomMaterial<T>>::mutate(|&mut(ref mut index, ref mut values)| if values.len() < 81 {
|
||||
values.push(parent_hash.clone())
|
||||
} else {
|
||||
values[*index as usize] = parent_hash.clone();
|
||||
*index = (*index + 1) % 81;
|
||||
});
|
||||
<Events<T>>::kill();
|
||||
}
|
||||
|
||||
/// Remove temporary "environment" entries in storage.
|
||||
pub fn finalize() -> T::Header {
|
||||
<RandomSeed<T>>::kill();
|
||||
<ExtrinsicCount<T>>::kill();
|
||||
<AllExtrinsicsLen<T>>::kill();
|
||||
|
||||
@@ -432,26 +437,13 @@ impl<T: Trait> Module<T> {
|
||||
<Digest<T>>::put(l);
|
||||
}
|
||||
|
||||
/// Calculate the current block's random seed.
|
||||
fn calculate_random() -> T::Hash {
|
||||
assert!(Self::block_number() > Zero::zero(), "Block number may never be zero");
|
||||
(0..81)
|
||||
.scan(
|
||||
Self::block_number() - One::one(),
|
||||
|c, _| { if *c > Zero::zero() { *c -= One::one() }; Some(*c)
|
||||
})
|
||||
.map(Self::block_hash)
|
||||
.triplet_mix()
|
||||
}
|
||||
|
||||
/// Get the basic externalities for this module, useful for tests.
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub fn externalities() -> TestExternalities<Blake2Hasher> {
|
||||
TestExternalities::new(map![
|
||||
twox_128(&<BlockHash<T>>::key_for(T::BlockNumber::zero())).to_vec() => [69u8; 32].encode(),
|
||||
twox_128(<Number<T>>::key()).to_vec() => T::BlockNumber::one().encode(),
|
||||
twox_128(<ParentHash<T>>::key()).to_vec() => [69u8; 32].encode(),
|
||||
twox_128(<RandomSeed<T>>::key()).to_vec() => T::Hash::default().encode()
|
||||
twox_128(<ParentHash<T>>::key()).to_vec() => [69u8; 32].encode()
|
||||
])
|
||||
}
|
||||
|
||||
@@ -475,11 +467,54 @@ impl<T: Trait> Module<T> {
|
||||
<ParentHash<T>>::put(n);
|
||||
}
|
||||
|
||||
/// Set the random seed to something in particular. Can be used as an alternative to
|
||||
/// `initialize` for tests that don't need to bother with the other environment entries.
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub fn set_random_seed(seed: T::Hash) {
|
||||
<RandomSeed<T>>::put(seed);
|
||||
/// 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.
|
||||
pub fn random_seed() -> T::Hash {
|
||||
Self::random(&[][..])
|
||||
}
|
||||
|
||||
/// Get a low-influence "random" value.
|
||||
///
|
||||
/// Being a deterministic block chain, real randomness is difficult to come by. This gives you something that
|
||||
/// approximates it. `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"[..])`.
|
||||
///
|
||||
/// This is initially implemented through a low-influence "triplet mix" convolution of previous block hash values.
|
||||
/// In the future it will be generated from a secure "VRF".
|
||||
///
|
||||
/// ### Security Notes
|
||||
/// 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 in advance by the block producer of this block (and, indeed, anyone
|
||||
/// who knows the block's `parent_hash`). However, it is mostly impossible for the producer of this block *alone*
|
||||
/// to influence the value of this hash. A sizable minority of dishonest and coordinating block producers would be
|
||||
/// required in order to affect this value. 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.
|
||||
///
|
||||
/// WARNING: Hashing the result of this function will remove any low-infleunce 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`.
|
||||
pub fn random(subject: &[u8]) -> T::Hash {
|
||||
let (index, hash_series) = <RandomMaterial<T>>::get();
|
||||
if hash_series.len() > 0 {
|
||||
// Always the case after block 1 is initialised.
|
||||
hash_series.iter()
|
||||
.cycle()
|
||||
.skip(index as usize)
|
||||
.take(81)
|
||||
.enumerate()
|
||||
.map(|(i, h)| (i as i8, subject, h).using_encoded(T::Hashing::hash))
|
||||
.triplet_mix()
|
||||
} else {
|
||||
T::Hash::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Increment a particular account's nonce by 1.
|
||||
|
||||
Reference in New Issue
Block a user