Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions frame/contracts/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,15 +268,17 @@ where

let mut queue = <DeletionQueue<T>>::get();

if let (Some(trie), true) = (queue.get(0), remaining_key_budget > 0) {
while !queue.is_empty() && remaining_key_budget > 0 {
// Cannot panic due to loop condition
let trie = &mut queue[0];
let outcome =
child::kill_storage(&child_trie_info(&trie.trie_id), Some(remaining_key_budget));
let keys_removed = match outcome {
// This happens when our budget wasn't large enough to remove all keys.
KillStorageResult::SomeRemaining(count) => count,
KillStorageResult::AllRemoved(count) => {
// We do not care to preserve order. The contract is deleted already and
// noone waits for the trie to be deleted.
// no one waits for the trie to be deleted.
queue.swap_remove(0);
count
},
Expand Down
53 changes: 53 additions & 0 deletions frame/contracts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,59 @@ fn lazy_removal_works() {
});
}

#[test]
fn lazy_batch_removal_works() {
let (code, hash) = compile_module::<Test>("self_destruct").unwrap();
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
let min_balance = <Test as Config>::Currency::minimum_balance();
let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance);
let mut tries: Vec<child::ChildInfo> = vec![];

for i in 0..3u8 {
assert_ok!(Contracts::instantiate_with_code(
Origin::signed(ALICE),
min_balance * 100,
GAS_LIMIT,
None,
code.clone(),
vec![],
vec![i],
),);

let addr = Contracts::contract_address(&ALICE, &hash, &[i]);
let info = <ContractInfoOf<Test>>::get(&addr).unwrap();
let trie = &info.child_trie_info();

// Put value into the contracts child trie
child::put(trie, &[99], &42);

// Terminate the contract. Contract info should be gone, but value should be still there
// as the lazy removal did not run, yet.
assert_ok!(Contracts::call(
Origin::signed(ALICE),
addr.clone(),
0,
GAS_LIMIT,
None,
vec![]
));
Comment on lines +1544 to +1551
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this end up terminating the contract?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look at the code in fixtures/self_destruct.wat then you should find the information, that by passing empty input to thte contract, it will call seal_terminate. And as far as I can understand text wasm format it does so :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. The contract terminates if called with an empty input.


assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
assert_matches!(child::get(trie, &[99]), Some(42));

tries.push(trie.clone())
}

// Run single lazy removal
Contracts::on_initialize(Weight::max_value());

// The single lazy removal should have removed all queued tries
for trie in tries.iter() {
assert_matches!(child::get::<i32>(trie, &[99]), None);
}
});
}

#[test]
fn lazy_removal_partial_remove_works() {
let (code, hash) = compile_module::<Test>("self_destruct").unwrap();
Expand Down
Loading