Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Make automatic storage deposits resistant against changing deposit prices #12083

Merged
merged 25 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1704,6 +1704,7 @@ pub type Executive = frame_executive::Executive<
type Migrations = (
pallet_nomination_pools::migration::v2::MigrateToV2<Runtime>,
pallet_alliance::migration::Migration<Runtime>,
pallet_contracts::Migration<Runtime>,
);

/// MMR helper types.
Expand Down
5 changes: 3 additions & 2 deletions frame/balances/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ use sp_runtime::{
AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedSub, MaybeSerializeDeserialize,
Saturating, StaticLookup, Zero,
},
ArithmeticError, DispatchError, RuntimeDebug,
ArithmeticError, DispatchError, FixedPointOperand, RuntimeDebug,
};
use sp_std::{cmp, fmt::Debug, mem, ops::BitOr, prelude::*, result};
pub use weights::WeightInfo;
Expand All @@ -212,7 +212,8 @@ pub mod pallet {
+ MaybeSerializeDeserialize
+ Debug
+ MaxEncodedLen
+ TypeInfo;
+ TypeInfo
+ FixedPointOperand;

/// Handler for the unbalanced reduction when removing a dust account.
type DustRemoval: OnUnbalanced<NegativeImbalance<Self, I>>;
Expand Down
55 changes: 55 additions & 0 deletions frame/contracts/fixtures/create_storage_and_call.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
;; This calls another contract as passed as its account id. It also creates some storage.
(module
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
(import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32)))
(import "seal1" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32)))
(import "env" "memory" (memory 1 1))

(func $assert (param i32)
(block $ok
(br_if $ok
(get_local 0)
)
(unreachable)
)
)

(func (export "deploy"))

(func (export "call")
;; store length of input buffer
(i32.store (i32.const 0) (i32.const 512))

;; copy input at address 4
(call $seal_input (i32.const 4) (i32.const 0))

;; create 4 byte of storage before calling
(call $seal_set_storage
(i32.const 0) ;; Pointer to storage key
(i32.const 0) ;; Pointer to value
(i32.const 4) ;; Size of value
)

;; call passed contract
(call $assert (i32.eqz
(call $seal_call
(i32.const 0) ;; No flags
(i32.const 8) ;; Pointer to "callee" address.
(i64.const 0) ;; How much gas to devote for the execution. 0 = all.
(i32.const 512) ;; Pointer to the buffer with value to transfer
(i32.const 4) ;; Pointer to input data buffer address
(i32.const 4) ;; Length of input data buffer
(i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
(i32.const 0) ;; Length is ignored in this case
)
))

;; create 8 byte of storage after calling
;; item of 12 bytes because we override 4 bytes
(call $seal_set_storage
(i32.const 0) ;; Pointer to storage key
(i32.const 0) ;; Pointer to value
(i32.const 12) ;; Size of value
)
)
)
18 changes: 9 additions & 9 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ where
module: WasmModule<T>,
data: Vec<u8>,
) -> Result<Contract<T>, &'static str> {
let value = T::Currency::minimum_balance();
let value = Pallet::<T>::min_balance();
T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
let salt = vec![0xff];
let addr = Contracts::<T>::contract_address(&caller, &module.hash, &salt);
Expand Down Expand Up @@ -257,7 +257,7 @@ benchmarks! {
let instance = Contract::<T>::with_caller(
whitelisted_caller(), WasmModule::sized(c, Location::Deploy), vec![],
)?;
let value = T::Currency::minimum_balance();
let value = Pallet::<T>::min_balance();
let origin = RawOrigin::Signed(instance.caller.clone());
let callee = instance.addr;
}: call(origin, callee, value, Weight::MAX, None, vec![])
Expand All @@ -280,7 +280,7 @@ benchmarks! {
let c in 0 .. Perbill::from_percent(49).mul_ceil(T::MaxCodeLen::get());
let s in 0 .. code::max_pages::<T>() * 64 * 1024;
let salt = vec![42u8; s as usize];
let value = T::Currency::minimum_balance();
let value = Pallet::<T>::min_balance();
let caller = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c, Location::Call);
Expand All @@ -307,7 +307,7 @@ benchmarks! {
instantiate {
let s in 0 .. code::max_pages::<T>() * 64 * 1024;
let salt = vec![42u8; s as usize];
let value = T::Currency::minimum_balance();
let value = Pallet::<T>::min_balance();
let caller = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::dummy();
Expand Down Expand Up @@ -338,7 +338,7 @@ benchmarks! {
let instance = Contract::<T>::with_caller(
whitelisted_caller(), WasmModule::dummy(), vec![],
)?;
let value = T::Currency::minimum_balance();
let value = Pallet::<T>::min_balance();
let origin = RawOrigin::Signed(instance.caller.clone());
let callee = instance.addr.clone();
let before = T::Currency::free_balance(&instance.account_id);
Expand Down Expand Up @@ -767,13 +767,13 @@ benchmarks! {
let instance = Contract::<T>::new(code, vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
assert_eq!(T::Currency::total_balance(&beneficiary), 0u32.into());
assert_eq!(T::Currency::free_balance(&instance.account_id), T::Currency::minimum_balance());
assert_eq!(T::Currency::free_balance(&instance.account_id), Pallet::<T>::min_balance());
assert_ne!(T::Currency::reserved_balance(&instance.account_id), 0u32.into());
}: call(origin, instance.addr.clone(), 0u32.into(), Weight::MAX, None, vec![])
verify {
if r > 0 {
assert_eq!(T::Currency::total_balance(&instance.account_id), 0u32.into());
assert_eq!(T::Currency::total_balance(&beneficiary), T::Currency::minimum_balance());
assert_eq!(T::Currency::total_balance(&beneficiary), Pallet::<T>::min_balance());
}
}

Expand Down Expand Up @@ -1469,7 +1469,7 @@ benchmarks! {
.collect::<Vec<_>>();
let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0);
let account_bytes = accounts.iter().flat_map(|x| x.encode()).collect();
let value = T::Currency::minimum_balance();
let value = Pallet::<T>::min_balance();
assert!(value > 0u32.into());
let value_bytes = value.encode();
let value_len = value_bytes.len();
Expand Down Expand Up @@ -1705,7 +1705,7 @@ benchmarks! {
let hash_len = hashes.get(0).map(|x| x.encode().len()).unwrap_or(0);
let hashes_bytes = hashes.iter().flat_map(|x| x.encode()).collect::<Vec<_>>();
let hashes_len = hashes_bytes.len();
let value = T::Currency::minimum_balance();
let value = Pallet::<T>::min_balance();
assert!(value > 0u32.into());
let value_bytes = value.encode();
let value_len = value_bytes.len();
Expand Down
23 changes: 13 additions & 10 deletions frame/contracts/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,15 @@ where
return Ok(output)
}

// Storage limit is enforced as late as possible (when the last frame returns) so that
// the ordering of storage accesses does not matter.
if self.frames.is_empty() {
let frame = top_frame_mut!(self);
athei marked this conversation as resolved.
Show resolved Hide resolved
frame.contract_info.load(&frame.account_id);
let contract = frame.contract_info.as_contract();
frame.nested_storage.enforce_limit(contract)?;
}

let frame = self.top_frame();
let account_id = &frame.account_id;
match (entry_point, delegated_code_hash) {
Expand Down Expand Up @@ -911,12 +920,7 @@ where
// it was invalidated.
frame.contract_info.load(account_id);
let mut contract = frame.contract_info.into_contract();
prev.nested_storage.absorb(
frame.nested_storage,
&self.origin,
account_id,
contract.as_mut(),
);
prev.nested_storage.absorb(frame.nested_storage, account_id, contract.as_mut());

// In case the contract wasn't terminated we need to persist changes made to it.
if let Some(contract) = contract {
Expand Down Expand Up @@ -954,7 +958,6 @@ where
let mut contract = self.first_frame.contract_info.as_contract();
self.storage_meter.absorb(
mem::take(&mut self.first_frame.nested_storage),
&self.origin,
&self.first_frame.account_id,
contract.as_deref_mut(),
);
Expand Down Expand Up @@ -2355,10 +2358,10 @@ mod tests {
let code_bob = MockLoader::insert(Call, |ctx, _| {
if ctx.input_data[0] == 0 {
let info = ctx.ext.contract_info();
assert_eq!(info.storage_deposit, 0);
info.storage_deposit = 42;
assert_eq!(info.storage_byte_deposit, 0);
info.storage_byte_deposit = 42;
assert_eq!(ctx.ext.call(Weight::zero(), CHARLIE, 0, vec![], true), exec_trapped());
assert_eq!(ctx.ext.contract_info().storage_deposit, 42);
assert_eq!(ctx.ext.contract_info().storage_byte_deposit, 42);
}
exec_success()
});
Expand Down
33 changes: 24 additions & 9 deletions frame/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@
mod gas;
mod benchmarking;
mod exec;
mod migration;
mod schedule;
mod storage;
mod wasm;

pub mod chain_extension;
pub mod migration;
pub mod weights;

#[cfg(test)]
Expand All @@ -109,7 +109,10 @@ use codec::{Encode, HasCompact};
use frame_support::{
dispatch::{DispatchClass, Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo},
ensure,
traits::{ConstU32, Contains, Currency, Get, Randomness, ReservableCurrency, Time},
traits::{
tokens::fungible::Inspect, ConstU32, Contains, Currency, Get, Randomness,
ReservableCurrency, Time,
},
weights::Weight,
BoundedVec, WeakBoundedVec,
};
Expand All @@ -126,6 +129,7 @@ use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};

pub use crate::{
exec::{Frame, VarSizedKey as StorageKey},
migration::Migration,
pallet::*,
schedule::{HostFnWeights, InstructionWeights, Limits, Schedule},
};
Expand Down Expand Up @@ -225,7 +229,7 @@ pub mod pallet {
use frame_system::pallet_prelude::*;

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(7);
const STORAGE_VERSION: StorageVersion = StorageVersion::new(8);

#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
Expand All @@ -236,11 +240,12 @@ pub mod pallet {
/// The time implementation used to supply timestamps to contracts through `seal_now`.
type Time: Time;

/// The generator used to supply randomness to contracts through `seal_random`.
/// The generator used to supply randomness to contracts through `seal_random`
type Randomness: Randomness<Self::Hash, Self::BlockNumber>;

/// The currency in which fees are paid and contract balances are held.
type Currency: ReservableCurrency<Self::AccountId>;
type Currency: ReservableCurrency<Self::AccountId>
+ Inspect<Self::AccountId, Balance = BalanceOf<Self>>;

/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
Expand Down Expand Up @@ -1036,7 +1041,7 @@ where
};
let schedule = T::Schedule::get();
let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
origin,
origin.clone(),
dest,
&mut gas_meter,
&mut storage_meter,
Expand All @@ -1045,7 +1050,11 @@ where
data,
debug_message,
);
InternalCallOutput { result, gas_meter, storage_deposit: storage_meter.into_deposit() }
InternalCallOutput {
result,
gas_meter,
storage_deposit: storage_meter.into_deposit(&origin),
}
}

/// Internal function that does the actual instantiation.
Expand Down Expand Up @@ -1089,7 +1098,7 @@ where
value.saturating_add(extra_deposit),
)?;
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
origin,
origin.clone(),
executable,
&mut gas_meter,
&mut storage_meter,
Expand All @@ -1100,17 +1109,23 @@ where
debug_message,
);
storage_deposit = storage_meter
.into_deposit()
.into_deposit(&origin)
.saturating_add(&StorageDeposit::Charge(extra_deposit));
result
};
InternalInstantiateOutput { result: try_exec(), gas_meter, storage_deposit }
}

/// Deposit a pallet contracts event. Handles the conversion to the overarching event type.
fn deposit_event(topics: Vec<T::Hash>, event: Event<T>) {
<frame_system::Pallet<T>>::deposit_event_indexed(
&topics,
<T as Config>::RuntimeEvent::from(event).into(),
)
}

/// Return the existential deposit of [`Config::Currency`].
fn min_balance() -> BalanceOf<T> {
<T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
}
}
Loading