mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-19 13:25:44 +00:00
contracts: Prevent PoV attack vector with minimal viable solution (#11372)
* Add ContractAccessWeight * Apply suggestions from code review Co-authored-by: Michael Müller <michi@parity.io> Co-authored-by: Michael Müller <michi@parity.io>
This commit is contained in:
committed by
GitHub
parent
e30c18d61d
commit
4b64c9085a
@@ -1059,8 +1059,11 @@ parameter_types! {
|
|||||||
pub const DepositPerByte: Balance = deposit(0, 1);
|
pub const DepositPerByte: Balance = deposit(0, 1);
|
||||||
pub const MaxValueSize: u32 = 16 * 1024;
|
pub const MaxValueSize: u32 = 16 * 1024;
|
||||||
// The lazy deletion runs inside on_initialize.
|
// The lazy deletion runs inside on_initialize.
|
||||||
pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO *
|
pub DeletionWeightLimit: Weight = RuntimeBlockWeights::get()
|
||||||
RuntimeBlockWeights::get().max_block;
|
.per_class
|
||||||
|
.get(DispatchClass::Normal)
|
||||||
|
.max_total
|
||||||
|
.unwrap_or(RuntimeBlockWeights::get().max_block);
|
||||||
// The weight needed for decoding the queue should be less or equal than a fifth
|
// The weight needed for decoding the queue should be less or equal than a fifth
|
||||||
// of the overall weight dedicated to the lazy deletion.
|
// of the overall weight dedicated to the lazy deletion.
|
||||||
pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / (
|
pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / (
|
||||||
@@ -1093,6 +1096,7 @@ impl pallet_contracts::Config for Runtime {
|
|||||||
type DeletionWeightLimit = DeletionWeightLimit;
|
type DeletionWeightLimit = DeletionWeightLimit;
|
||||||
type Schedule = Schedule;
|
type Schedule = Schedule;
|
||||||
type AddressGenerator = pallet_contracts::DefaultAddressGenerator;
|
type AddressGenerator = pallet_contracts::DefaultAddressGenerator;
|
||||||
|
type ContractAccessWeight = pallet_contracts::DefaultContractAccessWeight<RuntimeBlockWeights>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_sudo::Config for Runtime {
|
impl pallet_sudo::Config for Runtime {
|
||||||
|
|||||||
@@ -115,9 +115,9 @@ use frame_support::{
|
|||||||
dispatch::Dispatchable,
|
dispatch::Dispatchable,
|
||||||
ensure,
|
ensure,
|
||||||
traits::{Contains, Currency, Get, Randomness, ReservableCurrency, StorageVersion, Time},
|
traits::{Contains, Currency, Get, Randomness, ReservableCurrency, StorageVersion, Time},
|
||||||
weights::{GetDispatchInfo, Pays, PostDispatchInfo, Weight},
|
weights::{DispatchClass, GetDispatchInfo, Pays, PostDispatchInfo, Weight},
|
||||||
};
|
};
|
||||||
use frame_system::Pallet as System;
|
use frame_system::{limits::BlockWeights, Pallet as System};
|
||||||
use pallet_contracts_primitives::{
|
use pallet_contracts_primitives::{
|
||||||
Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult,
|
Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult,
|
||||||
ContractInstantiateResult, ExecReturnValue, GetStorageResult, InstantiateReturnValue,
|
ContractInstantiateResult, ExecReturnValue, GetStorageResult, InstantiateReturnValue,
|
||||||
@@ -126,7 +126,7 @@ use pallet_contracts_primitives::{
|
|||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
use sp_core::{crypto::UncheckedFrom, Bytes};
|
use sp_core::{crypto::UncheckedFrom, Bytes};
|
||||||
use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup};
|
use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup};
|
||||||
use sp_std::{fmt::Debug, prelude::*};
|
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};
|
||||||
|
|
||||||
type CodeHash<T> = <T as frame_system::Config>::Hash;
|
type CodeHash<T> = <T as frame_system::Config>::Hash;
|
||||||
type TrieId = Vec<u8>;
|
type TrieId = Vec<u8>;
|
||||||
@@ -193,6 +193,29 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A conservative implementation to be used for [`pallet::Config::ContractAccessWeight`].
|
||||||
|
///
|
||||||
|
/// This derives the weight from the [`BlockWeights`] passed as `B` and the `maxPovSize` passed
|
||||||
|
/// as `P`. The default value for `P` is the `maxPovSize` used by Polkadot and Kusama.
|
||||||
|
///
|
||||||
|
/// It simply charges from the weight meter pro rata: If loading the contract code would consume
|
||||||
|
/// 50% of the max storage proof then this charges 50% of the max block weight.
|
||||||
|
pub struct DefaultContractAccessWeight<B: Get<BlockWeights>, const P: u32 = 5_242_880>(
|
||||||
|
PhantomData<B>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<B: Get<BlockWeights>, const P: u32> Get<Weight> for DefaultContractAccessWeight<B, P> {
|
||||||
|
fn get() -> Weight {
|
||||||
|
let block_weights = B::get();
|
||||||
|
block_weights
|
||||||
|
.per_class
|
||||||
|
.get(DispatchClass::Normal)
|
||||||
|
.max_total
|
||||||
|
.unwrap_or(block_weights.max_block) /
|
||||||
|
Weight::from(P)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[frame_support::pallet]
|
#[frame_support::pallet]
|
||||||
pub mod pallet {
|
pub mod pallet {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -297,6 +320,27 @@ pub mod pallet {
|
|||||||
#[pallet::constant]
|
#[pallet::constant]
|
||||||
type DepositPerByte: Get<BalanceOf<Self>>;
|
type DepositPerByte: Get<BalanceOf<Self>>;
|
||||||
|
|
||||||
|
/// The weight per byte of code that is charged when loading a contract from storage.
|
||||||
|
///
|
||||||
|
/// Currently, FRAME only charges fees for computation incurred but not for PoV
|
||||||
|
/// consumption caused for storage access. This is usually not exploitable because
|
||||||
|
/// accessing storage carries some substantial weight costs, too. However in case
|
||||||
|
/// of contract code very much PoV consumption can be caused while consuming very little
|
||||||
|
/// computation. This could be used to keep the chain busy without paying the
|
||||||
|
/// proper fee for it. Until this is resolved we charge from the weight meter for
|
||||||
|
/// contract access.
|
||||||
|
///
|
||||||
|
/// For more information check out: <https://github.com/paritytech/substrate/issues/10301>
|
||||||
|
///
|
||||||
|
/// [`DefaultContractAccessWeight`] is a safe default to be used for polkadot or kusama
|
||||||
|
/// parachains.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// This is only relevant for parachains. Set to zero in case of a standalone chain.
|
||||||
|
#[pallet::constant]
|
||||||
|
type ContractAccessWeight: Get<Weight>;
|
||||||
|
|
||||||
/// The amount of balance a caller has to pay for each storage item.
|
/// The amount of balance a caller has to pay for each storage item.
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ use crate::{
|
|||||||
storage::Storage,
|
storage::Storage,
|
||||||
wasm::{PrefabWasmModule, ReturnCode as RuntimeReturnCode},
|
wasm::{PrefabWasmModule, ReturnCode as RuntimeReturnCode},
|
||||||
weights::WeightInfo,
|
weights::WeightInfo,
|
||||||
BalanceOf, Code, CodeStorage, Config, ContractInfoOf, DefaultAddressGenerator, Error, Pallet,
|
BalanceOf, Code, CodeStorage, Config, ContractInfoOf, DefaultAddressGenerator,
|
||||||
Schedule,
|
DefaultContractAccessWeight, Error, Pallet, Schedule,
|
||||||
};
|
};
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
@@ -287,6 +287,7 @@ impl Config for Test {
|
|||||||
type DepositPerByte = DepositPerByte;
|
type DepositPerByte = DepositPerByte;
|
||||||
type DepositPerItem = DepositPerItem;
|
type DepositPerItem = DepositPerItem;
|
||||||
type AddressGenerator = DefaultAddressGenerator;
|
type AddressGenerator = DefaultAddressGenerator;
|
||||||
|
type ContractAccessWeight = DefaultContractAccessWeight<BlockWeights>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]);
|
pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ use crate::{
|
|||||||
use frame_support::{
|
use frame_support::{
|
||||||
dispatch::{DispatchError, DispatchResult},
|
dispatch::{DispatchError, DispatchResult},
|
||||||
ensure,
|
ensure,
|
||||||
traits::ReservableCurrency,
|
traits::{Get, ReservableCurrency},
|
||||||
};
|
};
|
||||||
use sp_core::crypto::UncheckedFrom;
|
use sp_core::crypto::UncheckedFrom;
|
||||||
use sp_runtime::traits::BadOrigin;
|
use sp_runtime::traits::BadOrigin;
|
||||||
@@ -216,8 +216,12 @@ impl<T: Config> Token<T> for CodeToken {
|
|||||||
// size of the contract.
|
// size of the contract.
|
||||||
match *self {
|
match *self {
|
||||||
Reinstrument(len) => T::WeightInfo::reinstrument(len),
|
Reinstrument(len) => T::WeightInfo::reinstrument(len),
|
||||||
Load(len) => T::WeightInfo::call_with_code_per_byte(len)
|
Load(len) => {
|
||||||
.saturating_sub(T::WeightInfo::call_with_code_per_byte(0)),
|
let computation = T::WeightInfo::call_with_code_per_byte(len)
|
||||||
|
.saturating_sub(T::WeightInfo::call_with_code_per_byte(0));
|
||||||
|
let bandwith = T::ContractAccessWeight::get().saturating_mul(len.into());
|
||||||
|
computation.max(bandwith)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user