From 14fa752228cc568efa8c6eb691c386c7590a0515 Mon Sep 17 00:00:00 2001 From: snowmead Date: Thu, 17 Apr 2025 09:01:10 -0400 Subject: [PATCH 1/3] add randomness provider to pallet-evm get random in `block_randomness` for prevrandao --- frame/ethereum/src/mock.rs | 15 +++++++++++- frame/evm/precompile/dispatch/src/mock.rs | 16 ++++++++++++- .../precompile/storage-cleaner/src/mock.rs | 23 ++++++++++++++++++- frame/evm/src/lib.rs | 7 +++++- frame/evm/src/mock.rs | 15 +++++++++++- frame/evm/src/runner/stack.rs | 8 ++++++- precompiles/tests-external/lib.rs | 22 +++++++++++++++++- primitives/evm/src/lib.rs | 2 ++ template/runtime/src/lib.rs | 23 ++++++++++++++++++- 9 files changed, 123 insertions(+), 8 deletions(-) diff --git a/frame/ethereum/src/mock.rs b/frame/ethereum/src/mock.rs index eac999cf3c..2607b3d365 100644 --- a/frame/ethereum/src/mock.rs +++ b/frame/ethereum/src/mock.rs @@ -18,10 +18,11 @@ //! Test utilities use ethereum::{TransactionAction, TransactionSignature}; +use frame_system::pallet_prelude::BlockNumberFor; use rlp::RlpStream; // Substrate use frame_support::{derive_impl, parameter_types, traits::FindAuthor, ConsensusEngineId}; -use sp_core::{hashing::keccak_256, H160, H256, U256}; +use sp_core::{hashing::keccak_256, Hasher, H160, H256, U256}; use sp_runtime::{ traits::{Dispatchable, IdentityLookup}, AccountId32, BuildStorage, @@ -91,6 +92,17 @@ parameter_types! { pub const GasLimitStorageGrowthRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_STORAGE_GROWTH); } +pub struct RandomnessProvider; +impl frame_support::traits::Randomness<::Hash, BlockNumberFor> + for RandomnessProvider +{ + fn random(subject: &[u8]) -> (::Hash, BlockNumberFor) { + let output = ::Hashing::hash(subject); + let block_number = frame_system::Pallet::::block_number(); + (output, block_number) + } +} + #[derive_impl(pallet_evm::config_preludes::TestDefaultConfig)] impl pallet_evm::Config for Test { type AccountProvider = pallet_evm::FrameSystemAccountProvider; @@ -102,6 +114,7 @@ impl pallet_evm::Config for Test { type FindAuthor = FindAuthorTruncated; type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; type Timestamp = Timestamp; + type RandomnessProvider = RandomnessProvider; } #[derive_impl(crate::config_preludes::TestDefaultConfig)] diff --git a/frame/evm/precompile/dispatch/src/mock.rs b/frame/evm/precompile/dispatch/src/mock.rs index 98d21c8978..5acfaecd59 100644 --- a/frame/evm/precompile/dispatch/src/mock.rs +++ b/frame/evm/precompile/dispatch/src/mock.rs @@ -24,7 +24,8 @@ use frame_support::{ weights::Weight, ConsensusEngineId, }; -use sp_core::{H160, H256, U256}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_core::{Hasher, H160, H256, U256}; use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; use fp_evm::{ExitError, ExitReason, Transfer}; @@ -135,6 +136,18 @@ parameter_types! { pub WeightPerGas: Weight = Weight::from_parts(20_000, 0); pub SuicideQuickClearLimit: u32 = 0; } + +pub struct RandomnessProvider; +impl frame_support::traits::Randomness<::Hash, BlockNumberFor> + for RandomnessProvider +{ + fn random(subject: &[u8]) -> (::Hash, BlockNumberFor) { + let output = ::Hashing::hash(subject); + let block_number = frame_system::Pallet::::block_number(); + (output, block_number) + } +} + impl pallet_evm::Config for Test { type AccountProvider = pallet_evm::FrameSystemAccountProvider; type FeeCalculator = FixedGasPrice; @@ -162,6 +175,7 @@ impl pallet_evm::Config for Test { type GasLimitStorageGrowthRatio = (); type Timestamp = Timestamp; type WeightInfo = (); + type RandomnessProvider = RandomnessProvider; } pub(crate) struct MockHandle { diff --git a/frame/evm/precompile/storage-cleaner/src/mock.rs b/frame/evm/precompile/storage-cleaner/src/mock.rs index a63da6e9c0..96db9cc076 100644 --- a/frame/evm/precompile/storage-cleaner/src/mock.rs +++ b/frame/evm/precompile/storage-cleaner/src/mock.rs @@ -19,9 +19,10 @@ use crate::{StorageCleanerPrecompile, StorageCleanerPrecompileCall}; use frame_support::{parameter_types, weights::Weight}; +use frame_system::pallet_prelude::BlockNumberFor; use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, IdentityAddressMapping}; use precompile_utils::{precompile_set::*, testing::*}; -use sp_core::{ConstU32, H256, U256}; +use sp_core::{ConstU32, Hasher, H256, U256}; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, BuildStorage, @@ -125,6 +126,25 @@ parameter_types! { pub SuicideQuickClearLimit: u32 = 0; } +pub struct RandomnessProvider; +impl + frame_support::traits::Randomness< + ::Hash, + BlockNumberFor, + > for RandomnessProvider +{ + fn random( + subject: &[u8], + ) -> ( + ::Hash, + BlockNumberFor, + ) { + let output = ::Hashing::hash(subject); + let block_number = frame_system::Pallet::::block_number(); + (output, block_number) + } +} + impl pallet_evm::Config for Runtime { type AccountProvider = pallet_evm::FrameSystemAccountProvider; type FeeCalculator = (); @@ -149,6 +169,7 @@ impl pallet_evm::Config for Runtime { type Timestamp = Timestamp; type WeightInfo = (); type SuicideQuickClearLimit = SuicideQuickClearLimit; + type RandomnessProvider = RandomnessProvider; } /// Build test externalities, prepopulated with data for testing the precompile. diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index c1ec9a3a7b..6af658ba9b 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -208,6 +208,12 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + #[pallet::no_default] + type RandomnessProvider: frame_support::traits::Randomness< + sp_core::H256, + BlockNumberFor, + >; + /// EVM config used in the module. fn config() -> &'static EvmConfig { &CANCUN_CONFIG @@ -701,7 +707,6 @@ pub mod pallet { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(account_basic_weight) .saturating_add(min_gas_weight); - } total_weight diff --git a/frame/evm/src/mock.rs b/frame/evm/src/mock.rs index ebf0dc5cb9..1288134684 100644 --- a/frame/evm/src/mock.rs +++ b/frame/evm/src/mock.rs @@ -18,7 +18,8 @@ //! Test mock for unit tests and benchmarking use frame_support::{derive_impl, parameter_types, weights::Weight}; -use sp_core::{H160, U256}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_core::{Hasher, H160, U256}; use crate::{ FeeCalculator, IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult, @@ -75,6 +76,18 @@ impl crate::Config for Test { type Runner = crate::runner::stack::Runner; type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; type Timestamp = Timestamp; + type RandomnessProvider = RandomnessProvider; +} + +pub struct RandomnessProvider; +impl frame_support::traits::Randomness<::Hash, BlockNumberFor> + for RandomnessProvider +{ + fn random(subject: &[u8]) -> (::Hash, BlockNumberFor) { + let output = ::Hashing::hash(subject); + let block_number = frame_system::Pallet::::block_number(); + (output, block_number) + } } pub struct FixedGasPrice; diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index 688a80f269..a1895c85b2 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -963,7 +963,13 @@ where } fn block_randomness(&self) -> Option { - None + use frame_support::traits::Randomness; + use scale_codec::Encode; + + let current_block = frame_system::Pallet::::block_number(); + let output = T::RandomnessProvider::random(¤t_block.encode()).0; + + Some(output) } fn block_gas_limit(&self) -> U256 { diff --git a/precompiles/tests-external/lib.rs b/precompiles/tests-external/lib.rs index fd5a18e4a5..dc99be506f 100644 --- a/precompiles/tests-external/lib.rs +++ b/precompiles/tests-external/lib.rs @@ -27,7 +27,7 @@ use std::{cell::RefCell, rc::Rc}; use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::Everything, weights::Weight, }; -use sp_core::{H160, H256, U256}; +use sp_core::{Hasher, H160, H256, U256}; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, BuildStorage, Perbill, @@ -253,6 +253,26 @@ impl pallet_evm::Config for Runtime { type GasLimitStorageGrowthRatio = (); type Timestamp = Timestamp; type WeightInfo = pallet_evm::weights::SubstrateWeight; + type RandomnessProvider = RandomnessProvider; +} + +pub struct RandomnessProvider; +impl + frame_support::traits::Randomness< + ::Hash, + frame_system::pallet_prelude::BlockNumberFor, + > for RandomnessProvider +{ + fn random( + subject: &[u8], + ) -> ( + ::Hash, + frame_system::pallet_prelude::BlockNumberFor, + ) { + let output = ::Hashing::hash(subject); + let block_number = frame_system::Pallet::::block_number(); + (output, block_number) + } } parameter_types! { diff --git a/primitives/evm/src/lib.rs b/primitives/evm/src/lib.rs index 56b8aa3464..fbd81bdc66 100644 --- a/primitives/evm/src/lib.rs +++ b/primitives/evm/src/lib.rs @@ -75,6 +75,8 @@ pub const WRITE_PROOF_SIZE: u64 = 32; pub const IS_EMPTY_CHECK_PROOF_SIZE: u64 = 93; /// `AccountCodes` key size. 16 (hash) + 20 (key) pub const ACCOUNT_CODES_KEY_SIZE: u64 = 36; +/// System block number. +pub const SYSTEM_BLOCK_NUMBER_PROOF_SIZE: u64 = 32; pub enum AccessedStorage { AccountCodes(H160), diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index c7cac3d0a5..05a327d8e6 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -20,7 +20,7 @@ use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_grandpa::{AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList}; use sp_core::{ crypto::{ByteArray, KeyTypeId}, - ConstU128, OpaqueMetadata, H160, H256, U256, + ConstU128, Hasher, OpaqueMetadata, H160, H256, U256, }; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, @@ -50,6 +50,7 @@ use sp_genesis_builder::PresetId; use fp_account::EthereumSignature; use fp_evm::weight_per_gas; use fp_rpc::TransactionStatus; +use frame_system::pallet_prelude::BlockNumberFor; use pallet_ethereum::{Call::transact, PostLogContent, Transaction as EthereumTransaction}; use pallet_evm::{ Account as EVMAccount, EnsureAccountId20, FeeCalculator, IdentityAddressMapping, Runner, @@ -336,6 +337,25 @@ impl> FindAuthor for FindAuthorTruncated { } } +pub struct RandomnessProvider; +impl + frame_support::traits::Randomness< + ::Hash, + BlockNumberFor, + > for RandomnessProvider +{ + fn random( + subject: &[u8], + ) -> ( + ::Hash, + BlockNumberFor, + ) { + let output = ::Hashing::hash(subject); + let block_number = frame_system::Pallet::::block_number(); + (output, block_number) + } +} + const BLOCK_GAS_LIMIT: u64 = 75_000_000; const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; /// The maximum storage growth per block in bytes. @@ -374,6 +394,7 @@ impl pallet_evm::Config for Runtime { type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; type Timestamp = Timestamp; type WeightInfo = pallet_evm::weights::SubstrateWeight; + type RandomnessProvider = RandomnessProvider; } parameter_types! { From 98abedf631e1441a5c69852a8560cec1ae5be179 Mon Sep 17 00:00:00 2001 From: snowmead Date: Thu, 17 Apr 2025 11:08:28 -0400 Subject: [PATCH 2/3] remove unused const --- primitives/evm/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/primitives/evm/src/lib.rs b/primitives/evm/src/lib.rs index fbd81bdc66..56b8aa3464 100644 --- a/primitives/evm/src/lib.rs +++ b/primitives/evm/src/lib.rs @@ -75,8 +75,6 @@ pub const WRITE_PROOF_SIZE: u64 = 32; pub const IS_EMPTY_CHECK_PROOF_SIZE: u64 = 93; /// `AccountCodes` key size. 16 (hash) + 20 (key) pub const ACCOUNT_CODES_KEY_SIZE: u64 = 36; -/// System block number. -pub const SYSTEM_BLOCK_NUMBER_PROOF_SIZE: u64 = 32; pub enum AccessedStorage { AccountCodes(H160), From fc9ed63960b853b5792930f87e5af20d97b01f9b Mon Sep 17 00:00:00 2001 From: snowmead Date: Thu, 17 Apr 2025 16:00:01 -0400 Subject: [PATCH 3/3] add warning for insecure randomness --- template/runtime/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index 05a327d8e6..cc61c47b01 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -337,6 +337,7 @@ impl> FindAuthor for FindAuthorTruncated { } } +/// WARNING! THIS IS JUST AN EXAMPLE AND SHOULD NOT BE USED AS A MEANS TO RETRIEVE SECURE RANDOMNESS pub struct RandomnessProvider; impl frame_support::traits::Randomness<