diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 1b830105bb154..4c752045215f8 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -66,6 +66,7 @@ #[cfg(feature = "std")] use serde::Serialize; +use sp_io::hashing::blake2_256; #[cfg(feature = "runtime-benchmarks")] use sp_runtime::traits::TrailingZeroInput; use sp_runtime::{ @@ -1316,6 +1317,8 @@ impl Pallet { // populate environment ExecutionPhase::::put(Phase::Initialization); storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &0u32); + let entropy = (b"frame_system::initialize", parent_hash).using_encoded(blake2_256); + storage::unhashed::put(well_known_keys::INTRABLOCK_ENTROPY, &entropy[..]); >::put(number); >::put(digest); >::put(parent_hash); @@ -1365,6 +1368,7 @@ impl Pallet { ); ExecutionPhase::::kill(); AllExtrinsicsLen::::kill(); + storage::unhashed::kill(well_known_keys::INTRABLOCK_ENTROPY); // The following fields // @@ -1633,6 +1637,16 @@ impl Pallet { } } +/// Returns a 32 byte datum which is guaranteed to be universally unique. `entropy` is provided +/// as a facility to reduce the potential for precalculating results. +pub fn unique(entropy: impl Encode) -> [u8; 32] { + let mut last = [0u8; 32]; + sp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut last[..], 0); + let next = (b"frame_system::unique", entropy, last).using_encoded(blake2_256); + sp_io::storage::set(well_known_keys::INTRABLOCK_ENTROPY, &next.encode()); + next +} + /// Event handler which registers a provider when created. pub struct Provider(PhantomData); impl HandleLifetime for Provider { diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index 128231ccf95cb..d210bb609b191 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -34,6 +34,27 @@ fn origin_works() { assert_eq!(x.unwrap(), RawOrigin::::Signed(1u64)); } +#[test] +fn unique_datum_works() { + new_test_ext().execute_with(|| { + System::initialize(&1, &[0u8; 32].into(), &Default::default()); + assert!(sp_io::storage::exists(well_known_keys::INTRABLOCK_ENTROPY)); + + let h1 = unique(b""); + let h2 = unique(b""); + assert_ne!(h1, h2); + + let h3 = unique(b"Hello"); + assert_ne!(h2, h3); + + let h4 = unique(b"Hello"); + assert_ne!(h3, h4); + + System::finalize(); + assert!(!sp_io::storage::exists(well_known_keys::INTRABLOCK_ENTROPY)); + }); +} + #[test] fn stored_map_works() { new_test_ext().execute_with(|| { diff --git a/primitives/storage/src/lib.rs b/primitives/storage/src/lib.rs index 06995005d9f56..032a236a0ceb9 100644 --- a/primitives/storage/src/lib.rs +++ b/primitives/storage/src/lib.rs @@ -204,6 +204,9 @@ pub mod well_known_keys { /// Encodes to `0x3a65787472696e7369635f696e646578`. pub const EXTRINSIC_INDEX: &[u8] = b":extrinsic_index"; + /// Current intra-block entropy (a universally unique `[u8; 32]` value) is stored here. + pub const INTRABLOCK_ENTROPY: &[u8] = b":intrablock_entropy"; + /// Prefix of child storage keys. pub const CHILD_STORAGE_KEY_PREFIX: &[u8] = b":child_storage:";