mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-23 01:31:07 +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
@@ -22,12 +22,11 @@ use crate::{
|
||||
Result as ExtensionResult, RetVal, ReturnFlags, SysConfig,
|
||||
},
|
||||
exec::{FixSizedKey, Frame},
|
||||
storage::Storage,
|
||||
tests::test_utils::{get_contract, get_contract_checked},
|
||||
wasm::{Determinism, PrefabWasmModule, ReturnCode as RuntimeReturnCode},
|
||||
weights::WeightInfo,
|
||||
BalanceOf, Code, CodeStorage, Config, ContractInfoOf, DefaultAddressGenerator, DeletionQueue,
|
||||
Error, Pallet, Schedule,
|
||||
BalanceOf, Code, CodeStorage, Config, ContractInfo, ContractInfoOf, DefaultAddressGenerator,
|
||||
DeletionQueue, Error, Pallet, Schedule,
|
||||
};
|
||||
use assert_matches::assert_matches;
|
||||
use codec::Encode;
|
||||
@@ -37,8 +36,8 @@ use frame_support::{
|
||||
parameter_types,
|
||||
storage::child,
|
||||
traits::{
|
||||
BalanceStatus, ConstU32, ConstU64, Contains, Currency, Get, LockableCurrency, OnIdle,
|
||||
OnInitialize, ReservableCurrency, WithdrawReasons,
|
||||
ConstU32, ConstU64, Contains, Currency, ExistenceRequirement, Get, LockableCurrency,
|
||||
OnIdle, OnInitialize, ReservableCurrency, WithdrawReasons,
|
||||
},
|
||||
weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight},
|
||||
};
|
||||
@@ -51,7 +50,7 @@ use sp_runtime::{
|
||||
traits::{BlakeTwo256, Convert, Hash, IdentityLookup},
|
||||
AccountId32,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use crate as pallet_contracts;
|
||||
|
||||
@@ -76,9 +75,7 @@ frame_support::construct_runtime!(
|
||||
#[macro_use]
|
||||
pub mod test_utils {
|
||||
use super::{Balances, Hash, SysConfig, Test};
|
||||
use crate::{
|
||||
exec::AccountIdOf, storage::Storage, CodeHash, Config, ContractInfo, ContractInfoOf, Nonce,
|
||||
};
|
||||
use crate::{exec::AccountIdOf, CodeHash, Config, ContractInfo, ContractInfoOf, Nonce};
|
||||
use codec::Encode;
|
||||
use frame_support::traits::Currency;
|
||||
|
||||
@@ -87,9 +84,8 @@ pub mod test_utils {
|
||||
*counter += 1;
|
||||
*counter
|
||||
});
|
||||
let trie_id = Storage::<Test>::generate_trie_id(address, nonce);
|
||||
set_balance(address, <Test as Config>::Currency::minimum_balance() * 10);
|
||||
let contract = Storage::<Test>::new_contract(&address, trie_id, code_hash).unwrap();
|
||||
let contract = <ContractInfo<Test>>::new(&address, nonce, code_hash).unwrap();
|
||||
<ContractInfoOf<Test>>::insert(address, contract);
|
||||
}
|
||||
pub fn set_balance(who: &AccountIdOf<Test>, amount: u64) {
|
||||
@@ -518,7 +514,7 @@ fn calling_plain_account_fails() {
|
||||
fn instantiate_and_call_and_deposit_event() {
|
||||
let (wasm, code_hash) = compile_module::<Test>("event_and_return_on_deploy").unwrap();
|
||||
|
||||
ExtBuilder::default().existential_deposit(500).build().execute_with(|| {
|
||||
ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
|
||||
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
let min_balance = <Test as Config>::Currency::minimum_balance();
|
||||
let value = 100;
|
||||
@@ -551,9 +547,36 @@ fn instantiate_and_call_and_deposit_event() {
|
||||
.account_id;
|
||||
assert!(ContractInfoOf::<Test>::contains_key(&addr));
|
||||
|
||||
let contract = get_contract(&addr);
|
||||
let deposit_account = contract.deposit_account().deref();
|
||||
|
||||
assert_eq!(
|
||||
System::events(),
|
||||
vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::NewAccount {
|
||||
account: deposit_account.clone(),
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
|
||||
account: deposit_account.clone(),
|
||||
free_balance: 131,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: ALICE,
|
||||
to: deposit_account.clone(),
|
||||
amount: 131,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::NewAccount {
|
||||
@@ -578,14 +601,6 @@ fn instantiate_and_call_and_deposit_event() {
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
|
||||
who: addr.clone(),
|
||||
amount: min_balance,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
@@ -823,7 +838,7 @@ fn deploy_and_call_other_contract() {
|
||||
let (caller_wasm, _caller_code_hash) = compile_module::<Test>("caller_contract").unwrap();
|
||||
let (callee_wasm, callee_code_hash) = compile_module::<Test>("return_with_data").unwrap();
|
||||
|
||||
ExtBuilder::default().existential_deposit(500).build().execute_with(|| {
|
||||
ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
|
||||
let min_balance = <Test as Config>::Currency::minimum_balance();
|
||||
|
||||
// Create
|
||||
@@ -841,18 +856,7 @@ fn deploy_and_call_other_contract() {
|
||||
.result
|
||||
.unwrap()
|
||||
.account_id;
|
||||
Contracts::bare_instantiate(
|
||||
ALICE,
|
||||
100_000,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
Code::Upload(callee_wasm),
|
||||
0u32.to_le_bytes().encode(),
|
||||
vec![42],
|
||||
false,
|
||||
)
|
||||
.result
|
||||
.unwrap();
|
||||
Contracts::bare_upload_code(ALICE, callee_wasm, None, Determinism::Deterministic).unwrap();
|
||||
|
||||
let callee_addr = Contracts::contract_address(
|
||||
&caller_addr,
|
||||
@@ -875,9 +879,36 @@ fn deploy_and_call_other_contract() {
|
||||
callee_code_hash.as_ref().to_vec(),
|
||||
));
|
||||
|
||||
let callee = get_contract(&callee_addr);
|
||||
let deposit_account = callee.deposit_account().deref();
|
||||
|
||||
assert_eq!(
|
||||
System::events(),
|
||||
vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::NewAccount {
|
||||
account: deposit_account.clone(),
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
|
||||
account: deposit_account.clone(),
|
||||
free_balance: 131,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: ALICE,
|
||||
to: deposit_account.clone(),
|
||||
amount: 131,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::NewAccount {
|
||||
@@ -902,20 +933,12 @@ fn deploy_and_call_other_contract() {
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
|
||||
who: callee_addr.clone(),
|
||||
amount: min_balance,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: caller_addr.clone(),
|
||||
to: callee_addr.clone(),
|
||||
amount: 32768, // hard coded in wasm
|
||||
amount: 32768 // hardcoded in wasm
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
@@ -998,6 +1021,46 @@ fn delegate_call() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_allow_death_cannot_kill_account() {
|
||||
let (wasm, _code_hash) = compile_module::<Test>("dummy").unwrap();
|
||||
ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
|
||||
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
|
||||
// Instantiate the BOB contract.
|
||||
let addr = Contracts::bare_instantiate(
|
||||
ALICE,
|
||||
1_000,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
Code::Upload(wasm),
|
||||
vec![],
|
||||
vec![],
|
||||
false,
|
||||
)
|
||||
.result
|
||||
.unwrap()
|
||||
.account_id;
|
||||
|
||||
// Check that the BOB contract has been instantiated.
|
||||
get_contract(&addr);
|
||||
|
||||
let total_balance = <Test as Config>::Currency::total_balance(&addr);
|
||||
|
||||
assert_err!(
|
||||
<<Test as Config>::Currency as Currency<AccountId32>>::transfer(
|
||||
&addr,
|
||||
&ALICE,
|
||||
total_balance,
|
||||
ExistenceRequirement::AllowDeath,
|
||||
),
|
||||
pallet_balances::Error::<Test>::KeepAlive,
|
||||
);
|
||||
|
||||
assert_eq!(<Test as Config>::Currency::total_balance(&addr), total_balance);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_self_destruct_through_draning() {
|
||||
let (wasm, _code_hash) = compile_module::<Test>("drain").unwrap();
|
||||
@@ -1023,7 +1086,7 @@ fn cannot_self_destruct_through_draning() {
|
||||
get_contract(&addr);
|
||||
|
||||
// Call BOB which makes it send all funds to the zero address
|
||||
// The contract code asserts that the transfer was successful
|
||||
// The contract code asserts that the transfer fails with the correct error code
|
||||
assert_ok!(Contracts::call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
addr.clone(),
|
||||
@@ -1036,7 +1099,7 @@ fn cannot_self_destruct_through_draning() {
|
||||
// Make sure the account wasn't remove by sending all free balance away.
|
||||
assert_eq!(
|
||||
<Test as Config>::Currency::total_balance(&addr),
|
||||
<Test as Config>::Currency::minimum_balance(),
|
||||
1_000 + <Test as Config>::Currency::minimum_balance(),
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1093,96 +1156,10 @@ fn cannot_self_destruct_through_storage_refund_after_price_change() {
|
||||
|
||||
// Make sure the account wasn't removed by the refund
|
||||
assert_eq!(
|
||||
<Test as Config>::Currency::total_balance(&addr),
|
||||
<Test as Config>::Currency::total_balance(get_contract(&addr).deposit_account()),
|
||||
get_contract(&addr).total_deposit(),
|
||||
);
|
||||
assert_eq!(get_contract(&addr).extra_deposit(), 2,);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_self_destruct_by_refund_after_slash() {
|
||||
let (wasm, _code_hash) = compile_module::<Test>("store").unwrap();
|
||||
ExtBuilder::default().existential_deposit(500).build().execute_with(|| {
|
||||
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
let min_balance = <Test as Config>::Currency::minimum_balance();
|
||||
|
||||
let addr = Contracts::bare_instantiate(
|
||||
ALICE,
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
Code::Upload(wasm),
|
||||
vec![],
|
||||
vec![],
|
||||
false,
|
||||
)
|
||||
.result
|
||||
.unwrap()
|
||||
.account_id;
|
||||
|
||||
// create 100 more reserved balance
|
||||
assert_ok!(Contracts::call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
addr.clone(),
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
98u32.encode(),
|
||||
));
|
||||
|
||||
// Drop previous events
|
||||
initialize_block(2);
|
||||
|
||||
// slash parts of the 100 so that the next refund ould remove the account
|
||||
// because it the value it stored for `storage_deposit` becomes out of sync
|
||||
let _ = <Test as Config>::Currency::slash(&addr, 90);
|
||||
assert_eq!(<Test as Config>::Currency::total_balance(&addr), min_balance + 10);
|
||||
|
||||
// trigger a refund of 50 which would bring the contract below min when actually refunded
|
||||
assert_ok!(Contracts::call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
addr.clone(),
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
48u32.encode(),
|
||||
));
|
||||
|
||||
// Make sure the account kept the minimum balance and was not destroyed
|
||||
assert_eq!(<Test as Config>::Currency::total_balance(&addr), min_balance);
|
||||
|
||||
assert_eq!(
|
||||
System::events(),
|
||||
vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Slashed {
|
||||
who: addr.clone(),
|
||||
amount: 90,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
contract: addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&addr)],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::ReserveRepatriated {
|
||||
from: addr.clone(),
|
||||
to: ALICE,
|
||||
amount: 10,
|
||||
destination_status: BalanceStatus::Free,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
]
|
||||
);
|
||||
assert_eq!(get_contract(&addr).extra_deposit(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1252,7 +1229,7 @@ fn self_destruct_works() {
|
||||
.account_id;
|
||||
|
||||
// Check that the BOB contract has been instantiated.
|
||||
get_contract(&addr);
|
||||
let contract = get_contract(&addr);
|
||||
|
||||
// Drop all previous events
|
||||
initialize_block(2);
|
||||
@@ -1271,17 +1248,25 @@ fn self_destruct_works() {
|
||||
assert_eq!(Balances::total_balance(&addr), 0);
|
||||
|
||||
// check that the beneficiary (django) got remaining balance
|
||||
assert_eq!(Balances::free_balance(DJANGO), 1_000_000 + 100_000);
|
||||
let ed = <Test as Config>::Currency::minimum_balance();
|
||||
assert_eq!(Balances::free_balance(DJANGO), 1_000_000 + 100_000 + ed);
|
||||
|
||||
pretty_assertions::assert_eq!(
|
||||
System::events(),
|
||||
vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::KilledAccount {
|
||||
account: addr.clone()
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: addr.clone(),
|
||||
to: DJANGO,
|
||||
amount: 100_000,
|
||||
amount: 100_000 + ed,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
@@ -1304,17 +1289,16 @@ fn self_destruct_works() {
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::KilledAccount {
|
||||
account: addr.clone()
|
||||
account: contract.deposit_account().deref().clone(),
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::ReserveRepatriated {
|
||||
from: addr.clone(),
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: contract.deposit_account().deref().clone(),
|
||||
to: ALICE,
|
||||
amount: 1_000,
|
||||
destination_status: BalanceStatus::Free,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
@@ -2055,7 +2039,7 @@ fn lazy_removal_on_full_queue_works_on_initialize() {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Fill the deletion queue with dummy values, so that on_initialize attempts
|
||||
// to clear the queue
|
||||
Storage::<Test>::fill_queue_with_dummies();
|
||||
ContractInfo::<Test>::fill_queue_with_dummies();
|
||||
|
||||
let queue_len_initial = <DeletionQueue<Test>>::decode_len().unwrap_or(0);
|
||||
|
||||
@@ -2132,7 +2116,7 @@ fn lazy_removal_partial_remove_works() {
|
||||
// We create a contract with some extra keys above the weight limit
|
||||
let extra_keys = 7u32;
|
||||
let weight_limit = Weight::from_ref_time(5_000_000_000);
|
||||
let (_, max_keys) = Storage::<Test>::deletion_budget(1, weight_limit);
|
||||
let (_, max_keys) = ContractInfo::<Test>::deletion_budget(1, weight_limit);
|
||||
let vals: Vec<_> = (0..max_keys + extra_keys)
|
||||
.map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))
|
||||
.collect();
|
||||
@@ -2161,14 +2145,7 @@ fn lazy_removal_partial_remove_works() {
|
||||
|
||||
// Put value into the contracts child trie
|
||||
for val in &vals {
|
||||
Storage::<Test>::write(
|
||||
&info.trie_id,
|
||||
&val.0 as &FixSizedKey,
|
||||
Some(val.2.clone()),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
info.write(&val.0 as &FixSizedKey, Some(val.2.clone()), None, false).unwrap();
|
||||
}
|
||||
<ContractInfoOf<Test>>::insert(&addr, info.clone());
|
||||
|
||||
@@ -2201,7 +2178,7 @@ fn lazy_removal_partial_remove_works() {
|
||||
|
||||
ext.execute_with(|| {
|
||||
// Run the lazy removal
|
||||
let weight_used = Storage::<Test>::process_deletion_queue_batch(weight_limit);
|
||||
let weight_used = ContractInfo::<Test>::process_deletion_queue_batch(weight_limit);
|
||||
|
||||
// Weight should be exhausted because we could not even delete all keys
|
||||
assert_eq!(weight_used, weight_limit);
|
||||
@@ -2235,7 +2212,7 @@ fn lazy_removal_does_no_run_on_full_queue_and_full_block() {
|
||||
|
||||
// Fill the deletion queue with dummy values, so that on_initialize attempts
|
||||
// to clear the queue
|
||||
Storage::<Test>::fill_queue_with_dummies();
|
||||
ContractInfo::<Test>::fill_queue_with_dummies();
|
||||
|
||||
// Check that on_initialize() tries to perform lazy removal but removes nothing
|
||||
// as no more weight is left for that.
|
||||
@@ -2344,7 +2321,7 @@ fn lazy_removal_does_not_use_all_weight() {
|
||||
.account_id;
|
||||
|
||||
let info = get_contract(&addr);
|
||||
let (weight_per_key, max_keys) = Storage::<Test>::deletion_budget(1, weight_limit);
|
||||
let (weight_per_key, max_keys) = ContractInfo::<Test>::deletion_budget(1, weight_limit);
|
||||
|
||||
// We create a contract with one less storage item than we can remove within the limit
|
||||
let vals: Vec<_> = (0..max_keys - 1)
|
||||
@@ -2353,14 +2330,7 @@ fn lazy_removal_does_not_use_all_weight() {
|
||||
|
||||
// Put value into the contracts child trie
|
||||
for val in &vals {
|
||||
Storage::<Test>::write(
|
||||
&info.trie_id,
|
||||
&val.0 as &FixSizedKey,
|
||||
Some(val.2.clone()),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
info.write(&val.0 as &FixSizedKey, Some(val.2.clone()), None, false).unwrap();
|
||||
}
|
||||
<ContractInfoOf<Test>>::insert(&addr, info.clone());
|
||||
|
||||
@@ -2393,7 +2363,7 @@ fn lazy_removal_does_not_use_all_weight() {
|
||||
|
||||
ext.execute_with(|| {
|
||||
// Run the lazy removal
|
||||
let weight_used = Storage::<Test>::process_deletion_queue_batch(weight_limit);
|
||||
let weight_used = ContractInfo::<Test>::process_deletion_queue_batch(weight_limit);
|
||||
|
||||
// We have one less key in our trie than our weight limit suffices for
|
||||
assert_eq!(weight_used, weight_limit - weight_per_key);
|
||||
@@ -2427,7 +2397,7 @@ fn deletion_queue_full() {
|
||||
.account_id;
|
||||
|
||||
// fill the deletion queue up until its limit
|
||||
Storage::<Test>::fill_queue_with_dummies();
|
||||
ContractInfo::<Test>::fill_queue_with_dummies();
|
||||
|
||||
// Terminate the contract should fail
|
||||
assert_err_ignore_postinfo!(
|
||||
@@ -3196,14 +3166,12 @@ fn instantiate_with_zero_balance_works() {
|
||||
.account_id;
|
||||
|
||||
// Check that the BOB contract has been instantiated.
|
||||
get_contract(&addr);
|
||||
let contract = get_contract(&addr);
|
||||
let deposit_account = contract.deposit_account().deref();
|
||||
|
||||
// Make sure the account exists even though no free balance was send
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(&addr), 0,);
|
||||
assert_eq!(
|
||||
<Test as Config>::Currency::total_balance(&addr),
|
||||
<Test as Config>::Currency::minimum_balance(),
|
||||
);
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(&addr), min_balance);
|
||||
assert_eq!(<Test as Config>::Currency::total_balance(&addr), min_balance,);
|
||||
|
||||
assert_eq!(
|
||||
System::events(),
|
||||
@@ -3211,7 +3179,31 @@ fn instantiate_with_zero_balance_works() {
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::NewAccount {
|
||||
account: addr.clone()
|
||||
account: deposit_account.clone(),
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
|
||||
account: deposit_account.clone(),
|
||||
free_balance: min_balance,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: ALICE,
|
||||
to: deposit_account.clone(),
|
||||
amount: min_balance,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::NewAccount {
|
||||
account: addr.clone(),
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
@@ -3232,14 +3224,6 @@ fn instantiate_with_zero_balance_works() {
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
|
||||
who: addr.clone(),
|
||||
amount: min_balance,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
|
||||
@@ -3292,18 +3276,40 @@ fn instantiate_with_below_existential_deposit_works() {
|
||||
.account_id;
|
||||
|
||||
// Check that the BOB contract has been instantiated.
|
||||
get_contract(&addr);
|
||||
let contract = get_contract(&addr);
|
||||
let deposit_account = contract.deposit_account().deref();
|
||||
|
||||
// Make sure the account exists even though no free balance was send
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(&addr), 50,);
|
||||
assert_eq!(
|
||||
<Test as Config>::Currency::total_balance(&addr),
|
||||
<Test as Config>::Currency::minimum_balance() + 50,
|
||||
);
|
||||
// Make sure the account exists even though not enough free balance was send
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(&addr), min_balance + 50);
|
||||
assert_eq!(<Test as Config>::Currency::total_balance(&addr), min_balance + 50);
|
||||
|
||||
assert_eq!(
|
||||
System::events(),
|
||||
vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::NewAccount {
|
||||
account: deposit_account.clone(),
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
|
||||
account: deposit_account.clone(),
|
||||
free_balance: min_balance,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: ALICE,
|
||||
to: deposit_account.clone(),
|
||||
amount: min_balance,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::NewAccount {
|
||||
@@ -3328,14 +3334,6 @@ fn instantiate_with_below_existential_deposit_works() {
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
|
||||
who: addr.clone(),
|
||||
amount: min_balance,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
@@ -3436,6 +3434,9 @@ fn storage_deposit_works() {
|
||||
deposit -= refunded0;
|
||||
assert_eq!(get_contract(&addr).total_deposit(), deposit);
|
||||
|
||||
let contract = get_contract(&addr);
|
||||
let deposit_account = contract.deposit_account().deref();
|
||||
|
||||
assert_eq!(
|
||||
System::events(),
|
||||
vec![
|
||||
@@ -3460,15 +3461,7 @@ fn storage_deposit_works() {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: ALICE,
|
||||
to: addr.clone(),
|
||||
amount: charged0,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
|
||||
who: addr.clone(),
|
||||
to: deposit_account.clone(),
|
||||
amount: charged0,
|
||||
}),
|
||||
topics: vec![],
|
||||
@@ -3485,15 +3478,7 @@ fn storage_deposit_works() {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: ALICE,
|
||||
to: addr.clone(),
|
||||
amount: charged1,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
|
||||
who: addr.clone(),
|
||||
to: deposit_account.clone(),
|
||||
amount: charged1,
|
||||
}),
|
||||
topics: vec![],
|
||||
@@ -3508,11 +3493,10 @@ fn storage_deposit_works() {
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::ReserveRepatriated {
|
||||
from: addr.clone(),
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: deposit_account.clone(),
|
||||
to: ALICE,
|
||||
amount: refunded0,
|
||||
destination_status: BalanceStatus::Free,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
@@ -3610,11 +3594,10 @@ fn set_code_extrinsic() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_after_killed_account_needs_funding() {
|
||||
fn slash_cannot_kill_account() {
|
||||
let (wasm, _code_hash) = compile_module::<Test>("dummy").unwrap();
|
||||
ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
|
||||
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
let min_balance = <Test as Config>::Currency::minimum_balance();
|
||||
|
||||
let addr = Contracts::bare_instantiate(
|
||||
ALICE,
|
||||
@@ -3633,107 +3616,24 @@ fn call_after_killed_account_needs_funding() {
|
||||
// Drop previous events
|
||||
initialize_block(2);
|
||||
|
||||
// Destroy the account of the contract by slashing.
|
||||
// Slashing can actually happen if the contract takes part in staking.
|
||||
// It is a corner case and we accept the destruction of the account.
|
||||
// Try to destroy the account of the contract by slashing.
|
||||
// The account does not get destroyed because of the consumer reference.
|
||||
// Slashing can for example happen if the contract takes part in staking.
|
||||
let _ = <Test as Config>::Currency::slash(
|
||||
&addr,
|
||||
<Test as Config>::Currency::total_balance(&addr),
|
||||
);
|
||||
|
||||
// Sending below the minimum balance will fail the call because it needs to create the
|
||||
// account in order to send balance there.
|
||||
assert_err_ignore_postinfo!(
|
||||
Contracts::call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
addr.clone(),
|
||||
min_balance - 1,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
vec![],
|
||||
),
|
||||
<Error<Test>>::TransferFailed
|
||||
);
|
||||
|
||||
// Sending zero should work as it does not do a transfer
|
||||
assert_ok!(Contracts::call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
addr.clone(),
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
|
||||
// Sending minimum balance should work
|
||||
assert_ok!(Contracts::call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
addr.clone(),
|
||||
min_balance,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
System::events(),
|
||||
vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::KilledAccount {
|
||||
account: addr.clone()
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Slashed {
|
||||
who: addr.clone(),
|
||||
amount: min_balance + 700
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
contract: addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&addr)],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::System(frame_system::Event::NewAccount {
|
||||
account: addr.clone()
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Endowed {
|
||||
account: addr.clone(),
|
||||
free_balance: min_balance
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
|
||||
from: ALICE,
|
||||
to: addr.clone(),
|
||||
amount: min_balance
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
contract: addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&addr)],
|
||||
},
|
||||
]
|
||||
vec![EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Balances(pallet_balances::Event::Slashed {
|
||||
who: addr.clone(),
|
||||
amount: 700, // slash didn't remove the minimum balance
|
||||
}),
|
||||
topics: vec![],
|
||||
},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user