mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 01:07:57 +00:00
contracts: Don't rely on reserved balances keeping an account alive (#13369)
* Move storage deposits to their own account * Take ed for contract's account from origin * Apply suggestions from code review Co-authored-by: Cyrill Leutwiler <bigcyrill@hotmail.com> Co-authored-by: Sasha Gryaznov <hi@agryaznov.com> * Update stale docs * Use 16 bytes prefix for address derivation * Update frame/contracts/src/address.rs Co-authored-by: Cyrill Leutwiler <bigcyrill@hotmail.com> * Fix merge * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Update frame/contracts/primitives/src/lib.rs Co-authored-by: Sasha Gryaznov <hi@agryaznov.com> --------- Co-authored-by: Cyrill Leutwiler <bigcyrill@hotmail.com> Co-authored-by: Sasha Gryaznov <hi@agryaznov.com> Co-authored-by: command-bot <>
This commit is contained in:
committed by
GitHub
parent
292e5ee4e7
commit
330484bb19
@@ -17,9 +17,9 @@
|
||||
|
||||
use crate::{
|
||||
gas::GasMeter,
|
||||
storage::{self, Storage, WriteOutcome},
|
||||
storage::{self, DepositAccount, WriteOutcome},
|
||||
BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf, DebugBufferVec, Determinism, Error,
|
||||
Event, Nonce, Pallet as Contracts, Schedule,
|
||||
Event, Nonce, Pallet as Contracts, Schedule, System,
|
||||
};
|
||||
use frame_support::{
|
||||
crypto::ecdsa::ECDSAExt,
|
||||
@@ -492,7 +492,7 @@ enum CachedContract<T: Config> {
|
||||
///
|
||||
/// 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.
|
||||
Terminated,
|
||||
Terminated(DepositAccount<T>),
|
||||
}
|
||||
|
||||
impl<T: Config> CachedContract<T> {
|
||||
@@ -513,6 +513,15 @@ impl<T: Config> CachedContract<T> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Some` iff the contract is not `Cached::Invalidated`.
|
||||
fn deposit_account(&self) -> Option<&DepositAccount<T>> {
|
||||
match self {
|
||||
CachedContract::Cached(contract) => Some(contract.deposit_account()),
|
||||
CachedContract::Terminated(deposit_account) => Some(&deposit_account),
|
||||
CachedContract::Invalidated => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Frame<T> {
|
||||
@@ -591,7 +600,9 @@ impl<T: Config> CachedContract<T> {
|
||||
/// Terminate and return the contract info.
|
||||
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))
|
||||
let contract = get_cached_or_panic_after_load!(self);
|
||||
let deposit_account = contract.deposit_account().clone();
|
||||
get_cached_or_panic_after_load!(mem::replace(self, Self::Terminated(deposit_account)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -751,9 +762,7 @@ where
|
||||
input_data,
|
||||
salt,
|
||||
);
|
||||
let trie_id = Storage::<T>::generate_trie_id(&account_id, nonce);
|
||||
let contract =
|
||||
Storage::<T>::new_contract(&account_id, trie_id, *executable.code_hash())?;
|
||||
let contract = ContractInfo::new(&account_id, nonce, *executable.code_hash())?;
|
||||
(
|
||||
account_id,
|
||||
contract,
|
||||
@@ -873,7 +882,7 @@ where
|
||||
match (entry_point, delegated_code_hash) {
|
||||
(ExportedFunction::Constructor, _) => {
|
||||
// It is not allowed to terminate a contract inside its constructor.
|
||||
if matches!(frame.contract_info, CachedContract::Terminated) {
|
||||
if matches!(frame.contract_info, CachedContract::Terminated(_)) {
|
||||
return Err(Error::<T>::TerminatedInConstructor.into())
|
||||
}
|
||||
|
||||
@@ -962,11 +971,21 @@ where
|
||||
|
||||
// Record the storage meter changes of the nested call into the parent meter.
|
||||
// If the dropped frame's contract wasn't terminated we update the deposit counter
|
||||
// in its contract info. The load is necessary to to pull it from storage in case
|
||||
// in its contract info. The load is necessary to pull it from storage in case
|
||||
// it was invalidated.
|
||||
frame.contract_info.load(account_id);
|
||||
let deposit_account = frame
|
||||
.contract_info
|
||||
.deposit_account()
|
||||
.expect(
|
||||
"Is only `None` when the info is invalidated.
|
||||
We just re-loaded from storage which either makes the state `Cached` or `Terminated`.
|
||||
qed",
|
||||
)
|
||||
.clone();
|
||||
let mut contract = frame.contract_info.into_contract();
|
||||
prev.nested_storage.absorb(frame.nested_storage, account_id, contract.as_mut());
|
||||
prev.nested_storage
|
||||
.absorb(frame.nested_storage, deposit_account, contract.as_mut());
|
||||
|
||||
// In case the contract wasn't terminated we need to persist changes made to it.
|
||||
if let Some(contract) = contract {
|
||||
@@ -1001,10 +1020,14 @@ where
|
||||
if !persist {
|
||||
return
|
||||
}
|
||||
let deposit_account = self.first_frame.contract_info.deposit_account().expect(
|
||||
"Is only `None` when the info is invalidated. The first frame can't be invalidated.
|
||||
qed",
|
||||
).clone();
|
||||
let mut contract = self.first_frame.contract_info.as_contract();
|
||||
self.storage_meter.absorb(
|
||||
mem::take(&mut self.first_frame.nested_storage),
|
||||
&self.first_frame.account_id,
|
||||
deposit_account,
|
||||
contract.as_deref_mut(),
|
||||
);
|
||||
if let Some(contract) = contract {
|
||||
@@ -1182,19 +1205,21 @@ where
|
||||
}
|
||||
|
||||
fn terminate(&mut self, beneficiary: &AccountIdOf<Self::T>) -> Result<(), DispatchError> {
|
||||
use frame_support::traits::fungible::Inspect;
|
||||
if self.is_recursive() {
|
||||
return Err(Error::<T>::TerminatedWhileReentrant.into())
|
||||
}
|
||||
let frame = self.top_frame_mut();
|
||||
let info = frame.terminate();
|
||||
frame.nested_storage.terminate(&info);
|
||||
Storage::<T>::queue_trie_for_deletion(&info)?;
|
||||
<Stack<'a, T, E>>::transfer(
|
||||
ExistenceRequirement::AllowDeath,
|
||||
System::<T>::dec_consumers(&frame.account_id);
|
||||
T::Currency::transfer(
|
||||
&frame.account_id,
|
||||
beneficiary,
|
||||
T::Currency::free_balance(&frame.account_id),
|
||||
T::Currency::reducible_balance(&frame.account_id, false),
|
||||
ExistenceRequirement::AllowDeath,
|
||||
)?;
|
||||
info.queue_trie_for_deletion()?;
|
||||
ContractInfoOf::<T>::remove(&frame.account_id);
|
||||
E::remove_user(info.code_hash);
|
||||
Contracts::<T>::deposit_event(
|
||||
@@ -1212,19 +1237,19 @@ where
|
||||
}
|
||||
|
||||
fn get_storage(&mut self, key: &FixSizedKey) -> Option<Vec<u8>> {
|
||||
Storage::<T>::read(&self.top_frame_mut().contract_info().trie_id, key)
|
||||
self.top_frame_mut().contract_info().read(key)
|
||||
}
|
||||
|
||||
fn get_storage_transparent(&mut self, key: &VarSizedKey<T>) -> Option<Vec<u8>> {
|
||||
Storage::<T>::read(&self.top_frame_mut().contract_info().trie_id, key)
|
||||
self.top_frame_mut().contract_info().read(key)
|
||||
}
|
||||
|
||||
fn get_storage_size(&mut self, key: &FixSizedKey) -> Option<u32> {
|
||||
Storage::<T>::size(&self.top_frame_mut().contract_info().trie_id, key)
|
||||
self.top_frame_mut().contract_info().size(key)
|
||||
}
|
||||
|
||||
fn get_storage_size_transparent(&mut self, key: &VarSizedKey<T>) -> Option<u32> {
|
||||
Storage::<T>::size(&self.top_frame_mut().contract_info().trie_id, key)
|
||||
self.top_frame_mut().contract_info().size(key)
|
||||
}
|
||||
|
||||
fn set_storage(
|
||||
@@ -1234,8 +1259,7 @@ where
|
||||
take_old: bool,
|
||||
) -> Result<WriteOutcome, DispatchError> {
|
||||
let frame = self.top_frame_mut();
|
||||
Storage::<T>::write(
|
||||
&frame.contract_info.get(&frame.account_id).trie_id,
|
||||
frame.contract_info.get(&frame.account_id).write(
|
||||
key,
|
||||
value,
|
||||
Some(&mut frame.nested_storage),
|
||||
@@ -1250,8 +1274,7 @@ where
|
||||
take_old: bool,
|
||||
) -> Result<WriteOutcome, DispatchError> {
|
||||
let frame = self.top_frame_mut();
|
||||
Storage::<T>::write(
|
||||
&frame.contract_info.get(&frame.account_id).trie_id,
|
||||
frame.contract_info.get(&frame.account_id).write(
|
||||
key,
|
||||
value,
|
||||
Some(&mut frame.nested_storage),
|
||||
@@ -1438,7 +1461,6 @@ mod tests {
|
||||
use crate::{
|
||||
exec::ExportedFunction::*,
|
||||
gas::GasMeter,
|
||||
storage::Storage,
|
||||
tests::{
|
||||
test_utils::{get_balance, hash, place_contract, set_balance},
|
||||
ExtBuilder, RuntimeCall, RuntimeEvent as MetaEvent, Test, TestFilter, ALICE, BOB,
|
||||
@@ -1891,9 +1913,8 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
let executable =
|
||||
MockExecutable::from_storage(input_data_ch, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap();
|
||||
set_balance(&ALICE, min_balance * 10_000);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, min_balance).unwrap();
|
||||
|
||||
let result = MockStack::run_instantiate(
|
||||
ALICE,
|
||||
@@ -2231,7 +2252,7 @@ mod tests {
|
||||
// Check that the newly created account has the expected code hash and
|
||||
// there are instantiation event.
|
||||
assert_eq!(
|
||||
Storage::<Test>::code_hash(&instantiated_contract_address).unwrap(),
|
||||
ContractInfo::<Test>::load_code_hash(&instantiated_contract_address).unwrap(),
|
||||
dummy_ch
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -2273,7 +2294,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Check that the account has not been created.
|
||||
assert!(Storage::<Test>::code_hash(&instantiated_contract_address).is_none());
|
||||
assert!(ContractInfo::<Test>::load_code_hash(&instantiated_contract_address).is_none());
|
||||
assert!(events().is_empty());
|
||||
});
|
||||
}
|
||||
@@ -2332,7 +2353,7 @@ mod tests {
|
||||
// Check that the newly created account has the expected code hash and
|
||||
// there are instantiation event.
|
||||
assert_eq!(
|
||||
Storage::<Test>::code_hash(&instantiated_contract_address).unwrap(),
|
||||
ContractInfo::<Test>::load_code_hash(&instantiated_contract_address).unwrap(),
|
||||
dummy_ch
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -2374,7 +2395,7 @@ mod tests {
|
||||
set_balance(&ALICE, 1000);
|
||||
set_balance(&BOB, 100);
|
||||
place_contract(&BOB, instantiator_ch);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(100), 0).unwrap();
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(200), 0).unwrap();
|
||||
|
||||
assert_matches!(
|
||||
MockStack::run_call(
|
||||
@@ -2409,8 +2430,8 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
let executable =
|
||||
MockExecutable::from_storage(terminate_ch, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, 1000);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(100), 100).unwrap();
|
||||
set_balance(&ALICE, 10_000);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 100).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
MockStack::run_instantiate(
|
||||
@@ -2494,9 +2515,8 @@ mod tests {
|
||||
let min_balance = <Test as Config>::Currency::minimum_balance();
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
let executable = MockExecutable::from_storage(code, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap();
|
||||
set_balance(&ALICE, min_balance * 10_000);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, min_balance).unwrap();
|
||||
|
||||
let result = MockStack::run_instantiate(
|
||||
ALICE,
|
||||
@@ -2902,10 +2922,9 @@ mod tests {
|
||||
MockExecutable::from_storage(succ_fail_code, &schedule, &mut gas_meter).unwrap();
|
||||
let succ_succ_executable =
|
||||
MockExecutable::from_storage(succ_succ_code, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
set_balance(&ALICE, min_balance * 10_000);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&ALICE, Some(min_balance * 500), min_balance * 100)
|
||||
.unwrap();
|
||||
storage::meter::Meter::new(&ALICE, None, min_balance * 100).unwrap();
|
||||
|
||||
MockStack::run_instantiate(
|
||||
ALICE,
|
||||
|
||||
Reference in New Issue
Block a user