mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-06 06:48:01 +00:00
contracts: Remove state rent (#9669)
* Remove storage rent * Add storage migration * cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * Add migration for deletetion queue * Fix compilation * Increase gas supplied to out_of_gas to be sure that it won't deplete too early * cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs Co-authored-by: Parity Bot <admin@parity.io>
This commit is contained in:
committed by
GitHub
parent
13f3e25ebb
commit
522e77e243
@@ -16,11 +16,8 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
gas::GasMeter,
|
||||
rent::{Rent, RentStatus},
|
||||
storage::Storage,
|
||||
AccountCounter, AliveContractInfo, BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf,
|
||||
Error, Event, Pallet as Contracts, Schedule,
|
||||
gas::GasMeter, storage::Storage, AccountCounter, BalanceOf, CodeHash, Config, ContractInfo,
|
||||
ContractInfoOf, Error, Event, Pallet as Contracts, Schedule,
|
||||
};
|
||||
use frame_support::{
|
||||
dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable},
|
||||
@@ -28,18 +25,19 @@ use frame_support::{
|
||||
storage::{with_transaction, TransactionOutcome},
|
||||
traits::{Contains, Currency, ExistenceRequirement, Get, OriginTrait, Randomness, Time},
|
||||
weights::Weight,
|
||||
DefaultNoBound,
|
||||
};
|
||||
use frame_system::RawOrigin;
|
||||
use pallet_contracts_primitives::ExecReturnValue;
|
||||
use smallvec::{Array, SmallVec};
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use sp_runtime::{
|
||||
traits::{Convert, Saturating},
|
||||
Perbill,
|
||||
};
|
||||
use sp_runtime::traits::{Convert, Saturating};
|
||||
use sp_std::{marker::PhantomData, mem, prelude::*};
|
||||
|
||||
/// When fields are added to the [`ContractInfo`] that can change during execution this
|
||||
/// variable needs to be set to true. This will also force changes to the
|
||||
/// `in_memory_changes_not_discarded` test.
|
||||
const CONTRACT_INFO_CAN_CHANGE: bool = false;
|
||||
|
||||
pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
|
||||
pub type MomentOf<T> = <<T as Config>::Time as Time>::Moment;
|
||||
pub type SeedOf<T> = <T as frame_system::Config>::Hash;
|
||||
@@ -81,67 +79,6 @@ impl<T: Into<DispatchError>> From<T> for ExecError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Information needed for rent calculations that can be requested by a contract.
|
||||
#[derive(codec::Encode, DefaultNoBound)]
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
pub struct RentParams<T: Config> {
|
||||
/// The total balance of the contract. Includes the balance transferred from the caller.
|
||||
total_balance: BalanceOf<T>,
|
||||
/// The free balance of the contract. Includes the balance transferred from the caller.
|
||||
free_balance: BalanceOf<T>,
|
||||
/// See crate [`Contracts::subsistence_threshold()`].
|
||||
subsistence_threshold: BalanceOf<T>,
|
||||
/// See crate [`Config::DepositPerContract`].
|
||||
deposit_per_contract: BalanceOf<T>,
|
||||
/// See crate [`Config::DepositPerStorageByte`].
|
||||
deposit_per_storage_byte: BalanceOf<T>,
|
||||
/// See crate [`Config::DepositPerStorageItem`].
|
||||
deposit_per_storage_item: BalanceOf<T>,
|
||||
/// See crate [`Ext::rent_allowance()`].
|
||||
rent_allowance: BalanceOf<T>,
|
||||
/// See crate [`Config::RentFraction`].
|
||||
rent_fraction: Perbill,
|
||||
/// See crate [`AliveContractInfo::storage_size`].
|
||||
storage_size: u32,
|
||||
/// See crate [`Executable::aggregate_code_len()`].
|
||||
code_size: u32,
|
||||
/// See crate [`Executable::refcount()`].
|
||||
code_refcount: u32,
|
||||
/// Reserved for backwards compatible changes to this data structure.
|
||||
_reserved: Option<()>,
|
||||
}
|
||||
|
||||
impl<T> RentParams<T>
|
||||
where
|
||||
T: Config,
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
/// Derive new `RentParams` from the passed in data.
|
||||
///
|
||||
/// `value` is added to the current free and total balance of the contracts' account.
|
||||
fn new<E: Executable<T>>(
|
||||
account_id: &T::AccountId,
|
||||
value: &BalanceOf<T>,
|
||||
contract: &AliveContractInfo<T>,
|
||||
executable: &E,
|
||||
) -> Self {
|
||||
Self {
|
||||
total_balance: T::Currency::total_balance(account_id).saturating_add(*value),
|
||||
free_balance: T::Currency::free_balance(account_id).saturating_add(*value),
|
||||
subsistence_threshold: <Contracts<T>>::subsistence_threshold(),
|
||||
deposit_per_contract: T::DepositPerContract::get(),
|
||||
deposit_per_storage_byte: T::DepositPerStorageByte::get(),
|
||||
deposit_per_storage_item: T::DepositPerStorageItem::get(),
|
||||
rent_allowance: contract.rent_allowance,
|
||||
rent_fraction: T::RentFraction::get(),
|
||||
storage_size: contract.storage_size,
|
||||
code_size: executable.aggregate_code_len(),
|
||||
code_refcount: executable.refcount(),
|
||||
_reserved: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface that provides access to the external environment in which the
|
||||
/// smart-contract is executed.
|
||||
///
|
||||
@@ -197,25 +134,6 @@ pub trait Ext: sealing::Sealed {
|
||||
/// call stack.
|
||||
fn terminate(&mut self, beneficiary: &AccountIdOf<Self::T>) -> Result<(), DispatchError>;
|
||||
|
||||
/// Restores the given destination contract sacrificing the current one.
|
||||
///
|
||||
/// Since this function removes the self contract eagerly, if succeeded, no further actions
|
||||
/// should be performed on this `Ext` instance.
|
||||
///
|
||||
/// This function will fail if the same contract is present
|
||||
/// on the contract call stack.
|
||||
///
|
||||
/// # Return Value
|
||||
///
|
||||
/// Result<(CallerCodeSize, DestCodeSize), (DispatchError, CallerCodeSize, DestCodesize)>
|
||||
fn restore_to(
|
||||
&mut self,
|
||||
dest: AccountIdOf<Self::T>,
|
||||
code_hash: CodeHash<Self::T>,
|
||||
rent_allowance: BalanceOf<Self::T>,
|
||||
delta: Vec<StorageKey>,
|
||||
) -> Result<(), DispatchError>;
|
||||
|
||||
/// Transfer some amount of funds into the specified account.
|
||||
fn transfer(&mut self, to: &AccountIdOf<Self::T>, value: BalanceOf<Self::T>) -> DispatchResult;
|
||||
|
||||
@@ -249,8 +167,8 @@ pub trait Ext: sealing::Sealed {
|
||||
/// Returns the minimum balance that is required for creating an account.
|
||||
fn minimum_balance(&self) -> BalanceOf<Self::T>;
|
||||
|
||||
/// Returns the deposit required to create a tombstone upon contract eviction.
|
||||
fn tombstone_deposit(&self) -> BalanceOf<Self::T>;
|
||||
/// Returns the deposit required to instantiate a contract.
|
||||
fn contract_deposit(&self) -> BalanceOf<Self::T>;
|
||||
|
||||
/// Returns a random number for the current block with the given subject.
|
||||
fn random(&self, subject: &[u8]) -> (SeedOf<Self::T>, BlockNumberOf<Self::T>);
|
||||
@@ -260,12 +178,6 @@ pub trait Ext: sealing::Sealed {
|
||||
/// There should not be any duplicates in `topics`.
|
||||
fn deposit_event(&mut self, topics: Vec<TopicOf<Self::T>>, data: Vec<u8>);
|
||||
|
||||
/// Set rent allowance of the contract
|
||||
fn set_rent_allowance(&mut self, rent_allowance: BalanceOf<Self::T>);
|
||||
|
||||
/// Rent allowance of the contract
|
||||
fn rent_allowance(&mut self) -> BalanceOf<Self::T>;
|
||||
|
||||
/// Returns the current block number.
|
||||
fn block_number(&self) -> BlockNumberOf<Self::T>;
|
||||
|
||||
@@ -278,12 +190,6 @@ pub trait Ext: sealing::Sealed {
|
||||
/// Get a reference to the schedule used by the current call.
|
||||
fn schedule(&self) -> &Schedule<Self::T>;
|
||||
|
||||
/// Information needed for rent calculations.
|
||||
fn rent_params(&self) -> &RentParams<Self::T>;
|
||||
|
||||
/// Information about the required deposit and resulting rent.
|
||||
fn rent_status(&mut self, at_refcount: u32) -> RentStatus<Self::T>;
|
||||
|
||||
/// Get a mutable reference to the nested gas meter.
|
||||
fn gas_meter(&mut self) -> &mut GasMeter<Self::T>;
|
||||
|
||||
@@ -336,9 +242,6 @@ pub trait Executable<T: Config>: Sized {
|
||||
/// Does not charge from the gas meter. Do not call in contexts where this is important.
|
||||
fn from_storage_noinstr(code_hash: CodeHash<T>) -> Result<Self, DispatchError>;
|
||||
|
||||
/// Decrements the refcount by one and deletes the code if it drops to zero.
|
||||
fn drop_from_storage(self);
|
||||
|
||||
/// Increment the refcount by one. Fails if the code does not exist on-chain.
|
||||
///
|
||||
/// Returns the size of the original code.
|
||||
@@ -387,23 +290,6 @@ pub trait Executable<T: Config>: Sized {
|
||||
|
||||
// The number of contracts using this executable.
|
||||
fn refcount(&self) -> u32;
|
||||
|
||||
/// The storage that is occupied by the instrumented executable and its pristine source.
|
||||
///
|
||||
/// The returned size is already divided by the number of users who share the code.
|
||||
/// This is essentially `aggregate_code_len() / refcount()`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This works with the current in-memory value of refcount. When calling any contract
|
||||
/// without refetching this from storage the result can be inaccurate as it might be
|
||||
/// working with a stale value. Usually this inaccuracy is tolerable.
|
||||
fn occupied_storage(&self) -> u32 {
|
||||
// We disregard the size of the struct itself as the size is completely
|
||||
// dominated by the code size.
|
||||
let len = self.aggregate_code_len();
|
||||
len.checked_div(self.refcount()).unwrap_or(len)
|
||||
}
|
||||
}
|
||||
|
||||
/// The complete call stack of a contract execution.
|
||||
@@ -461,8 +347,6 @@ pub struct Frame<T: Config> {
|
||||
contract_info: CachedContract<T>,
|
||||
/// The amount of balance transferred by the caller as part of the call.
|
||||
value_transferred: BalanceOf<T>,
|
||||
/// Snapshotted rent information that can be copied to the contract if requested.
|
||||
rent_params: RentParams<T>,
|
||||
/// Determines whether this is a call or instantiate frame.
|
||||
entry_point: ExportedFunction,
|
||||
/// The gas meter capped to the supplied gas limit.
|
||||
@@ -479,7 +363,7 @@ enum FrameArgs<'a, T: Config, E> {
|
||||
/// The account id of the contract that is to be called.
|
||||
dest: T::AccountId,
|
||||
/// If `None` the contract info needs to be reloaded from storage.
|
||||
cached_info: Option<AliveContractInfo<T>>,
|
||||
cached_info: Option<ContractInfo<T>>,
|
||||
},
|
||||
Instantiate {
|
||||
/// The contract or signed origin which instantiates the new contract.
|
||||
@@ -496,12 +380,12 @@ enum FrameArgs<'a, T: Config, E> {
|
||||
/// Describes the different states of a contract as contained in a `Frame`.
|
||||
enum CachedContract<T: Config> {
|
||||
/// The cached contract is up to date with the in-storage value.
|
||||
Cached(AliveContractInfo<T>),
|
||||
Cached(ContractInfo<T>),
|
||||
/// A recursive call into the same contract did write to the contract info.
|
||||
///
|
||||
/// In this case the cached contract is stale and needs to be reloaded from storage.
|
||||
Invalidated,
|
||||
/// The current contract executed `terminate` or `restore_to` and removed the contract.
|
||||
/// The current contract executed `terminate` and removed the contract.
|
||||
///
|
||||
/// In this case a reload is neither allowed nor possible. Please note that recursive
|
||||
/// calls cannot remove a contract as this is checked and denied.
|
||||
@@ -510,13 +394,8 @@ enum CachedContract<T: Config> {
|
||||
|
||||
impl<T: Config> Frame<T> {
|
||||
/// Return the `contract_info` of the current contract.
|
||||
fn contract_info(&mut self) -> &mut AliveContractInfo<T> {
|
||||
self.contract_info.as_alive(&self.account_id)
|
||||
}
|
||||
|
||||
/// Invalidate and return the `contract_info` of the current contract.
|
||||
fn invalidate(&mut self) -> AliveContractInfo<T> {
|
||||
self.contract_info.invalidate(&self.account_id)
|
||||
fn contract_info(&mut self) -> &mut ContractInfo<T> {
|
||||
self.contract_info.get(&self.account_id)
|
||||
}
|
||||
|
||||
/// Terminate and return the `contract_info` of the current contract.
|
||||
@@ -525,7 +404,7 @@ impl<T: Config> Frame<T> {
|
||||
///
|
||||
/// Under no circumstances the contract is allowed to access the `contract_info` after
|
||||
/// a call to this function. This would constitute a programming error in the exec module.
|
||||
fn terminate(&mut self) -> AliveContractInfo<T> {
|
||||
fn terminate(&mut self) -> ContractInfo<T> {
|
||||
self.contract_info.terminate(&self.account_id)
|
||||
}
|
||||
}
|
||||
@@ -540,7 +419,7 @@ macro_rules! get_cached_or_panic_after_load {
|
||||
} else {
|
||||
panic!(
|
||||
"It is impossible to remove a contract that is on the call stack;\
|
||||
See implementations of terminate and restore_to;\
|
||||
See implementations of terminate;\
|
||||
Therefore fetching a contract will never fail while using an account id
|
||||
that is currently active on the call stack;\
|
||||
qed"
|
||||
@@ -553,28 +432,21 @@ impl<T: Config> CachedContract<T> {
|
||||
/// Load the `contract_info` from storage if necessary.
|
||||
fn load(&mut self, account_id: &T::AccountId) {
|
||||
if let CachedContract::Invalidated = self {
|
||||
let contract =
|
||||
<ContractInfoOf<T>>::get(&account_id).and_then(|contract| contract.get_alive());
|
||||
let contract = <ContractInfoOf<T>>::get(&account_id);
|
||||
if let Some(contract) = contract {
|
||||
*self = CachedContract::Cached(contract);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the cached contract_info as alive contract info.
|
||||
fn as_alive(&mut self, account_id: &T::AccountId) -> &mut AliveContractInfo<T> {
|
||||
/// Return the cached contract_info.
|
||||
fn get(&mut self, account_id: &T::AccountId) -> &mut ContractInfo<T> {
|
||||
self.load(account_id);
|
||||
get_cached_or_panic_after_load!(self)
|
||||
}
|
||||
|
||||
/// Invalidate and return the contract info.
|
||||
fn invalidate(&mut self, account_id: &T::AccountId) -> AliveContractInfo<T> {
|
||||
self.load(account_id);
|
||||
get_cached_or_panic_after_load!(mem::replace(self, Self::Invalidated))
|
||||
}
|
||||
|
||||
/// Terminate and return the contract info.
|
||||
fn terminate(&mut self, account_id: &T::AccountId) -> AliveContractInfo<T> {
|
||||
fn terminate(&mut self, account_id: &T::AccountId) -> ContractInfo<T> {
|
||||
self.load(account_id);
|
||||
get_cached_or_panic_after_load!(mem::replace(self, Self::Terminated))
|
||||
}
|
||||
@@ -695,23 +567,11 @@ where
|
||||
let contract = if let Some(contract) = cached_info {
|
||||
contract
|
||||
} else {
|
||||
<ContractInfoOf<T>>::get(&dest)
|
||||
.ok_or(<Error<T>>::ContractNotFound.into())
|
||||
.and_then(|contract| {
|
||||
contract.get_alive().ok_or(<Error<T>>::ContractIsTombstone)
|
||||
})?
|
||||
<ContractInfoOf<T>>::get(&dest).ok_or(<Error<T>>::ContractNotFound)?
|
||||
};
|
||||
|
||||
let executable = E::from_storage(contract.code_hash, schedule, gas_meter)?;
|
||||
|
||||
// This charges the rent and denies access to a contract that is in need of
|
||||
// eviction by returning `None`. We cannot evict eagerly here because those
|
||||
// changes would be rolled back in case this contract is called by another
|
||||
// contract.
|
||||
// See: https://github.com/paritytech/substrate/issues/6439#issuecomment-648754324
|
||||
let contract =
|
||||
Rent::<T, E>::charge(&dest, contract, executable.occupied_storage())?
|
||||
.ok_or(Error::<T>::RentNotPaid)?;
|
||||
(dest, contract, executable, ExportedFunction::Call)
|
||||
},
|
||||
FrameArgs::Instantiate { sender, trie_seed, executable, salt } => {
|
||||
@@ -728,12 +588,6 @@ where
|
||||
};
|
||||
|
||||
let frame = Frame {
|
||||
rent_params: RentParams::new(
|
||||
&account_id,
|
||||
&value_transferred,
|
||||
&contract_info,
|
||||
&executable,
|
||||
),
|
||||
value_transferred,
|
||||
contract_info: CachedContract::Cached(contract_info),
|
||||
account_id,
|
||||
@@ -756,18 +610,17 @@ where
|
||||
return Err(Error::<T>::MaxCallDepthReached.into())
|
||||
}
|
||||
|
||||
// We need to make sure that changes made to the contract info are not discarded.
|
||||
// See the `in_memory_changes_not_discarded` test for more information.
|
||||
// We do not store on instantiate because we do not allow to call into a contract
|
||||
// from its own constructor.
|
||||
let frame = self.top_frame();
|
||||
if let (CachedContract::Cached(contract), ExportedFunction::Call) =
|
||||
(&frame.contract_info, frame.entry_point)
|
||||
{
|
||||
<ContractInfoOf<T>>::insert(
|
||||
frame.account_id.clone(),
|
||||
ContractInfo::Alive(contract.clone()),
|
||||
);
|
||||
if CONTRACT_INFO_CAN_CHANGE {
|
||||
// We need to make sure that changes made to the contract info are not discarded.
|
||||
// See the `in_memory_changes_not_discarded` test for more information.
|
||||
// We do not store on instantiate because we do not allow to call into a contract
|
||||
// from its own constructor.
|
||||
let frame = self.top_frame();
|
||||
if let (CachedContract::Cached(contract), ExportedFunction::Call) =
|
||||
(&frame.contract_info, frame.entry_point)
|
||||
{
|
||||
<ContractInfoOf<T>>::insert(frame.account_id.clone(), contract.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let nested_meter =
|
||||
@@ -784,12 +637,6 @@ where
|
||||
fn run(&mut self, executable: E, input_data: Vec<u8>) -> Result<ExecReturnValue, ExecError> {
|
||||
let entry_point = self.top_frame().entry_point;
|
||||
let do_transaction = || {
|
||||
// Cache the value before calling into the constructor because that
|
||||
// consumes the value. If the constructor creates additional contracts using
|
||||
// the same code hash we still charge the "1 block rent" as if they weren't
|
||||
// spawned. This is OK as overcharging is always safe.
|
||||
let occupied_storage = executable.occupied_storage();
|
||||
|
||||
// Every call or instantiate also optionally transferres balance.
|
||||
self.initial_transfer()?;
|
||||
|
||||
@@ -808,16 +655,6 @@ where
|
||||
return Err(Error::<T>::TerminatedInConstructor.into())
|
||||
}
|
||||
|
||||
// Collect the rent for the first block to prevent the creation of very large
|
||||
// contracts that never intended to pay for even one block.
|
||||
// This also makes sure that it is above the subsistence threshold
|
||||
// in order to keep up the guarantuee that we always leave a tombstone behind
|
||||
// with the exception of a contract that called `seal_terminate`.
|
||||
let contract =
|
||||
Rent::<T, E>::charge(&account_id, frame.invalidate(), occupied_storage)?
|
||||
.ok_or(Error::<T>::NewContractNotFunded)?;
|
||||
frame.contract_info = CachedContract::Cached(contract);
|
||||
|
||||
// Deposit an instantiation event.
|
||||
deposit_event::<T>(vec![], Event::Instantiated(self.caller().clone(), account_id));
|
||||
}
|
||||
@@ -877,7 +714,7 @@ where
|
||||
// because that case is already handled by the optimization above. Only the first
|
||||
// cache needs to be invalidated because that one will invalidate the next cache
|
||||
// when it is popped from the stack.
|
||||
<ContractInfoOf<T>>::insert(account_id, ContractInfo::Alive(contract));
|
||||
<ContractInfoOf<T>>::insert(account_id, contract);
|
||||
if let Some(c) = self.frames_mut().skip(1).find(|f| f.account_id == *account_id) {
|
||||
c.contract_info = CachedContract::Invalidated;
|
||||
}
|
||||
@@ -897,10 +734,7 @@ where
|
||||
return
|
||||
}
|
||||
if let CachedContract::Cached(contract) = &self.first_frame.contract_info {
|
||||
<ContractInfoOf<T>>::insert(
|
||||
&self.first_frame.account_id,
|
||||
ContractInfo::Alive(contract.clone()),
|
||||
);
|
||||
<ContractInfoOf<T>>::insert(&self.first_frame.account_id, contract.clone());
|
||||
}
|
||||
if let Some(counter) = self.account_counter {
|
||||
<AccountCounter<T>>::set(counter);
|
||||
@@ -1111,38 +945,6 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn restore_to(
|
||||
&mut self,
|
||||
dest: AccountIdOf<Self::T>,
|
||||
code_hash: CodeHash<Self::T>,
|
||||
rent_allowance: BalanceOf<Self::T>,
|
||||
delta: Vec<StorageKey>,
|
||||
) -> Result<(), DispatchError> {
|
||||
if self.is_recursive() {
|
||||
return Err(Error::<T>::TerminatedWhileReentrant.into())
|
||||
}
|
||||
let frame = self.top_frame_mut();
|
||||
let origin_contract = frame.contract_info().clone();
|
||||
let account_id = frame.account_id.clone();
|
||||
let result = Rent::<T, E>::restore_to(
|
||||
&account_id,
|
||||
origin_contract,
|
||||
dest.clone(),
|
||||
code_hash.clone(),
|
||||
rent_allowance,
|
||||
delta,
|
||||
&mut frame.nested_meter,
|
||||
);
|
||||
if let Ok(_) = result {
|
||||
deposit_event::<Self::T>(
|
||||
vec![],
|
||||
Event::Restored(account_id, dest, code_hash, rent_allowance),
|
||||
);
|
||||
frame.terminate();
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn transfer(&mut self, to: &T::AccountId, value: BalanceOf<T>) -> DispatchResult {
|
||||
Self::transfer(true, false, &self.top_frame().account_id, to, value)
|
||||
}
|
||||
@@ -1152,9 +954,8 @@ where
|
||||
}
|
||||
|
||||
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) -> DispatchResult {
|
||||
let block_number = self.block_number;
|
||||
let frame = self.top_frame_mut();
|
||||
Storage::<T>::write(block_number, frame.contract_info(), &key, value)
|
||||
Storage::<T>::write(frame.contract_info(), &key, value)
|
||||
}
|
||||
|
||||
fn address(&self) -> &T::AccountId {
|
||||
@@ -1185,8 +986,8 @@ where
|
||||
T::Currency::minimum_balance()
|
||||
}
|
||||
|
||||
fn tombstone_deposit(&self) -> BalanceOf<T> {
|
||||
T::TombstoneDeposit::get()
|
||||
fn contract_deposit(&self) -> BalanceOf<T> {
|
||||
T::ContractDeposit::get()
|
||||
}
|
||||
|
||||
fn deposit_event(&mut self, topics: Vec<T::Hash>, data: Vec<u8>) {
|
||||
@@ -1196,14 +997,6 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
fn set_rent_allowance(&mut self, rent_allowance: BalanceOf<T>) {
|
||||
self.top_frame_mut().contract_info().rent_allowance = rent_allowance;
|
||||
}
|
||||
|
||||
fn rent_allowance(&mut self) -> BalanceOf<T> {
|
||||
self.top_frame_mut().contract_info().rent_allowance
|
||||
}
|
||||
|
||||
fn block_number(&self) -> T::BlockNumber {
|
||||
self.block_number
|
||||
}
|
||||
@@ -1220,24 +1013,6 @@ where
|
||||
&self.schedule
|
||||
}
|
||||
|
||||
fn rent_params(&self) -> &RentParams<Self::T> {
|
||||
&self.top_frame().rent_params
|
||||
}
|
||||
|
||||
fn rent_status(&mut self, at_refcount: u32) -> RentStatus<Self::T> {
|
||||
let frame = self.top_frame_mut();
|
||||
let balance = T::Currency::free_balance(&frame.account_id);
|
||||
let code_size = frame.rent_params.code_size;
|
||||
let refcount = frame.rent_params.code_refcount;
|
||||
<Rent<T, E>>::rent_status(
|
||||
&balance,
|
||||
&frame.contract_info(),
|
||||
code_size,
|
||||
refcount,
|
||||
at_refcount,
|
||||
)
|
||||
}
|
||||
|
||||
fn gas_meter(&mut self) -> &mut GasMeter<Self::T> {
|
||||
&mut self.top_frame_mut().nested_meter
|
||||
}
|
||||
@@ -1304,7 +1079,7 @@ mod tests {
|
||||
use frame_support::{assert_err, assert_ok};
|
||||
use frame_system::{EventRecord, Phase};
|
||||
use pallet_contracts_primitives::ReturnFlags;
|
||||
use pretty_assertions::{assert_eq, assert_ne};
|
||||
use pretty_assertions::assert_eq;
|
||||
use sp_core::Bytes;
|
||||
use sp_runtime::{
|
||||
traits::{BadOrigin, Hash},
|
||||
@@ -1400,12 +1175,6 @@ mod tests {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn refcount(code_hash: &CodeHash<Test>) -> u32 {
|
||||
LOADER.with(|loader| {
|
||||
loader.borrow().map.get(code_hash).expect("code_hash does not exist").refcount()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Executable<Test> for MockExecutable {
|
||||
@@ -1428,10 +1197,6 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn drop_from_storage(self) {
|
||||
MockLoader::decrement_refcount(self.code_hash);
|
||||
}
|
||||
|
||||
fn add_user(
|
||||
code_hash: CodeHash<Test>,
|
||||
_: &mut GasMeter<Test>,
|
||||
@@ -1546,7 +1311,7 @@ mod tests {
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, return_ch);
|
||||
place_contract(&dest, return_ch);
|
||||
set_balance(&origin, 100);
|
||||
let balance = get_balance(&dest);
|
||||
|
||||
@@ -1563,9 +1328,7 @@ mod tests {
|
||||
|
||||
assert!(!output.is_success());
|
||||
assert_eq!(get_balance(&origin), 100);
|
||||
|
||||
// the rent is still charged
|
||||
assert!(get_balance(&dest) < balance);
|
||||
assert_eq!(get_balance(&dest), balance);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2066,147 +1829,12 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rent_allowance() {
|
||||
let rent_allowance_ch = MockLoader::insert(Constructor, |ctx, _| {
|
||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||
let allowance = subsistence * 3;
|
||||
assert_eq!(ctx.ext.rent_allowance(), <BalanceOf<Test>>::max_value());
|
||||
ctx.ext.set_rent_allowance(allowance);
|
||||
assert_eq!(ctx.ext.rent_allowance(), allowance);
|
||||
exec_success()
|
||||
});
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
let executable =
|
||||
MockExecutable::from_storage(rent_allowance_ch, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, subsistence * 10);
|
||||
|
||||
let result = MockStack::run_instantiate(
|
||||
ALICE,
|
||||
executable,
|
||||
&mut gas_meter,
|
||||
&schedule,
|
||||
subsistence * 5,
|
||||
vec![],
|
||||
&[],
|
||||
None,
|
||||
);
|
||||
assert_matches!(result, Ok(_));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rent_params_works() {
|
||||
let code_hash = MockLoader::insert(Call, |ctx, executable| {
|
||||
let address = ctx.ext.address();
|
||||
let contract =
|
||||
<ContractInfoOf<Test>>::get(address).and_then(|c| c.get_alive()).unwrap();
|
||||
assert_eq!(ctx.ext.rent_params(), &RentParams::new(address, &0, &contract, executable));
|
||||
exec_success()
|
||||
});
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, subsistence * 10);
|
||||
place_contract(&BOB, code_hash);
|
||||
MockStack::run_call(ALICE, BOB, &mut gas_meter, &schedule, 0, vec![], None).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rent_params_snapshotted() {
|
||||
let code_hash = MockLoader::insert(Call, |ctx, executable| {
|
||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||
let address = ctx.ext.address();
|
||||
let contract =
|
||||
<ContractInfoOf<Test>>::get(address).and_then(|c| c.get_alive()).unwrap();
|
||||
let rent_params = RentParams::new(address, &0, &contract, executable);
|
||||
|
||||
// Changing the allowance during the call: rent params stay unchanged.
|
||||
let allowance = 42;
|
||||
assert_ne!(allowance, rent_params.rent_allowance);
|
||||
ctx.ext.set_rent_allowance(allowance);
|
||||
assert_eq!(ctx.ext.rent_params(), &rent_params);
|
||||
|
||||
// Creating another instance from the same code_hash increases the refcount.
|
||||
// This is also not reflected in the rent params.
|
||||
assert_eq!(MockLoader::refcount(&executable.code_hash), 1);
|
||||
ctx.ext
|
||||
.instantiate(0, executable.code_hash, subsistence * 25, vec![], &[])
|
||||
.unwrap();
|
||||
assert_eq!(MockLoader::refcount(&executable.code_hash), 2);
|
||||
assert_eq!(ctx.ext.rent_params(), &rent_params);
|
||||
|
||||
exec_success()
|
||||
});
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, subsistence * 100);
|
||||
place_contract(&BOB, code_hash);
|
||||
MockStack::run_call(
|
||||
ALICE,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&schedule,
|
||||
subsistence * 50,
|
||||
vec![],
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rent_status_works() {
|
||||
let code_hash = MockLoader::insert(Call, |ctx, _| {
|
||||
assert_eq!(
|
||||
ctx.ext.rent_status(0),
|
||||
RentStatus {
|
||||
max_deposit: 80000,
|
||||
current_deposit: 80000,
|
||||
custom_refcount_deposit: None,
|
||||
max_rent: 32,
|
||||
current_rent: 32,
|
||||
custom_refcount_rent: None,
|
||||
_reserved: None,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.ext.rent_status(1),
|
||||
RentStatus {
|
||||
max_deposit: 80000,
|
||||
current_deposit: 80000,
|
||||
custom_refcount_deposit: Some(80000),
|
||||
max_rent: 32,
|
||||
current_rent: 32,
|
||||
custom_refcount_rent: Some(32),
|
||||
_reserved: None,
|
||||
}
|
||||
);
|
||||
exec_success()
|
||||
});
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, subsistence * 10);
|
||||
place_contract(&BOB, code_hash);
|
||||
MockStack::run_call(ALICE, BOB, &mut gas_meter, &schedule, 0, vec![], None).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_memory_changes_not_discarded() {
|
||||
// Remove this assert and fill out the "DO" stubs once fields are added to the
|
||||
// contract info that can be modified during exection.
|
||||
assert!(!CONTRACT_INFO_CAN_CHANGE);
|
||||
|
||||
// Call stack: BOB -> CHARLIE (trap) -> BOB' (success)
|
||||
// This tests verfies some edge case of the contract info cache:
|
||||
// We change some value in our contract info before calling into a contract
|
||||
@@ -2217,13 +1845,9 @@ mod tests {
|
||||
// are made before calling into CHARLIE are not discarded.
|
||||
let code_bob = MockLoader::insert(Call, |ctx, _| {
|
||||
if ctx.input_data[0] == 0 {
|
||||
let original_allowance = ctx.ext.rent_allowance();
|
||||
let changed_allowance = <BalanceOf<Test>>::max_value() / 2;
|
||||
assert_ne!(original_allowance, changed_allowance);
|
||||
ctx.ext.set_rent_allowance(changed_allowance);
|
||||
// DO: modify medata (ContractInfo) of own contract through ctx.ext functions
|
||||
assert_eq!(ctx.ext.call(0, CHARLIE, 0, vec![], true), exec_trapped());
|
||||
assert_eq!(ctx.ext.rent_allowance(), changed_allowance);
|
||||
assert_ne!(ctx.ext.rent_allowance(), original_allowance);
|
||||
// DO: check that the value is not discarded (query via ctx.ext)
|
||||
}
|
||||
exec_success()
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user