mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 22:37:57 +00:00
seal: Prevent contracts from going below subsistence (#6623)
* seal: Do not allow transfers to bring total balance below subsistence deposit This also reworks the rent system to take the total balance into account when evaluating whether the account is above the subsistence deposit. * Fix nits from review * Fix typo * Do not enforce subsistence when called from EOA * Rename CallOrigin to TransactorKind * Add debug asserts to check the invariants of a plain account transactor * Fix typo Co-authored-by: Sergei Shulepov <sergei@parity.io> Co-authored-by: Sergei Shulepov <sergei@parity.io>
This commit is contained in:
committed by
GitHub
parent
64114267b2
commit
efc69d8219
@@ -16,17 +16,15 @@
|
||||
|
||||
use super::{CodeHash, Config, ContractAddressFor, Event, RawEvent, Trait,
|
||||
TrieId, BalanceOf, ContractInfo, TrieIdGenerator};
|
||||
use crate::gas::{Gas, GasMeter, Token};
|
||||
use crate::rent;
|
||||
use crate::storage;
|
||||
use crate::{gas::{Gas, GasMeter, Token}, rent, storage, Error, ContractInfoOf};
|
||||
use bitflags::bitflags;
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use sp_runtime::traits::{Bounded, Zero, Convert};
|
||||
use sp_runtime::traits::{Bounded, Zero, Convert, Saturating};
|
||||
use frame_support::{
|
||||
dispatch::DispatchError,
|
||||
traits::{ExistenceRequirement, Currency, Time, Randomness},
|
||||
weights::Weight,
|
||||
ensure, StorageMap,
|
||||
};
|
||||
|
||||
pub type AccountIdOf<T> = <T as frame_system::Trait>::AccountId;
|
||||
@@ -46,6 +44,15 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes whether we deal with a contract or a plain account.
|
||||
pub enum TransactorKind {
|
||||
/// Transaction was initiated from a plain account. That can be either be through a
|
||||
/// signed transaction or through RPC.
|
||||
PlainAccount,
|
||||
/// The call was initiated by a contract account.
|
||||
Contract,
|
||||
}
|
||||
|
||||
/// Output of a contract call or instantiation which ran to completion.
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
pub struct ExecReturnValue {
|
||||
@@ -320,6 +327,7 @@ where
|
||||
Err("contract has been evicted")?
|
||||
};
|
||||
|
||||
let transactor_kind = self.transactor_kind();
|
||||
let caller = self.self_account.clone();
|
||||
let dest_trie_id = contract_info.and_then(|i| i.as_alive().map(|i| i.trie_id.clone()));
|
||||
|
||||
@@ -328,6 +336,7 @@ where
|
||||
transfer(
|
||||
gas_meter,
|
||||
TransferCause::Call,
|
||||
transactor_kind,
|
||||
&caller,
|
||||
&dest,
|
||||
value,
|
||||
@@ -372,6 +381,7 @@ where
|
||||
Err("not enough gas to pay base instantiate fee")?
|
||||
}
|
||||
|
||||
let transactor_kind = self.transactor_kind();
|
||||
let caller = self.self_account.clone();
|
||||
let dest = T::DetermineContractAddress::contract_address_for(
|
||||
code_hash,
|
||||
@@ -399,6 +409,7 @@ where
|
||||
transfer(
|
||||
gas_meter,
|
||||
TransferCause::Instantiate,
|
||||
transactor_kind,
|
||||
&caller,
|
||||
&dest,
|
||||
endowment,
|
||||
@@ -466,6 +477,17 @@ where
|
||||
&self.self_account == account ||
|
||||
self.caller.map_or(false, |caller| caller.is_live(account))
|
||||
}
|
||||
|
||||
fn transactor_kind(&self) -> TransactorKind {
|
||||
if self.depth == 0 {
|
||||
debug_assert!(self.self_trie_id.is_none());
|
||||
debug_assert!(self.caller.is_none());
|
||||
debug_assert!(ContractInfoOf::<T>::get(&self.self_account).is_none());
|
||||
TransactorKind::PlainAccount
|
||||
} else {
|
||||
TransactorKind::Contract
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||
@@ -519,6 +541,7 @@ enum TransferCause {
|
||||
fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
cause: TransferCause,
|
||||
origin: TransactorKind,
|
||||
transactor: &T::AccountId,
|
||||
dest: &T::AccountId,
|
||||
value: BalanceOf<T>,
|
||||
@@ -526,6 +549,7 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
|
||||
) -> Result<(), DispatchError> {
|
||||
use self::TransferCause::*;
|
||||
use self::TransferFeeKind::*;
|
||||
use self::TransactorKind::*;
|
||||
|
||||
let token = {
|
||||
let kind: TransferFeeKind = match cause {
|
||||
@@ -545,10 +569,19 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
|
||||
Err("not enough gas to pay transfer fee")?
|
||||
}
|
||||
|
||||
// Only ext_terminate is allowed to bring the sender below the existential deposit
|
||||
let existence_requirement = match cause {
|
||||
Terminate => ExistenceRequirement::AllowDeath,
|
||||
_ => ExistenceRequirement::KeepAlive,
|
||||
// Only ext_terminate is allowed to bring the sender below the subsistence
|
||||
// threshold or even existential deposit.
|
||||
let existence_requirement = match (cause, origin) {
|
||||
(Terminate, _) => ExistenceRequirement::AllowDeath,
|
||||
(_, Contract) => {
|
||||
ensure!(
|
||||
T::Currency::total_balance(transactor).saturating_sub(value) >=
|
||||
ctx.config.subsistence_threshold(),
|
||||
Error::<T>::InsufficientBalance,
|
||||
);
|
||||
ExistenceRequirement::KeepAlive
|
||||
},
|
||||
(_, PlainAccount) => ExistenceRequirement::KeepAlive,
|
||||
};
|
||||
T::Currency::transfer(transactor, dest, value, existence_requirement)?;
|
||||
|
||||
@@ -630,6 +663,7 @@ where
|
||||
transfer(
|
||||
gas_meter,
|
||||
TransferCause::Call,
|
||||
TransactorKind::Contract,
|
||||
&self.ctx.self_account.clone(),
|
||||
&to,
|
||||
value,
|
||||
@@ -654,6 +688,7 @@ where
|
||||
transfer(
|
||||
gas_meter,
|
||||
TransferCause::Terminate,
|
||||
TransactorKind::Contract,
|
||||
&self_id,
|
||||
beneficiary,
|
||||
value,
|
||||
|
||||
Reference in New Issue
Block a user