Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit ce449a5

Browse files
achimccatheiParity Botagryaznov
authored
Contracts pallet: removal on idle (#11202)
* on_initialize -> on_idle * use remaining_weight info * no weight_limit for on_idle * call on_idle in tests * attempt to fix tests * run on_initiaize when queue full * add on_idle to weight info * add on_idle weight info to on_idle hook * add basic test for on_initialize with full queue * disbale check for all keys gone in full queue, full block test * queue_deth as usize, add comment * comment was removed by accident * Update frame/contracts/src/lib.rs Co-authored-by: Alexander Theißen <[email protected]> * cargo +nightly fmt * update lazy_removal_does_no_run_on_full_queue_and_full_block * remove changes in weights.rs * weights on_idle -> on_process_deletion_queue_batch * use block number for on_idle * use BlockNumber for on_initialize * cargo run --quiet --profile=production --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark pallet --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 * Update frame/contracts/src/lib.rs Co-authored-by: Alexander Theißen <[email protected]> * remove outcommented code * add check that queue still full for test * cargo fmt * cargo +nightly fmt * Update frame/contracts/src/benchmarking/mod.rs Co-authored-by: Alexander Gryaznov <[email protected]> * fix weights.rs * add lazy_removal_does_no_run_on_low_remaining_weight test * Apply suggestions from code review Co-authored-by: Alexander Gryaznov <[email protected]> Co-authored-by: Alexander Theißen <[email protected]> Co-authored-by: Parity Bot <[email protected]> Co-authored-by: Alexander Gryaznov <[email protected]>
1 parent 7ca5e87 commit ce449a5

File tree

5 files changed

+200
-74
lines changed

5 files changed

+200
-74
lines changed

frame/contracts/src/benchmarking/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ benchmarks! {
202202
<BalanceOf<T> as codec::HasCompact>::Type: Clone + Eq + PartialEq + sp_std::fmt::Debug + scale_info::TypeInfo + codec::Encode,
203203
}
204204

205-
// The base weight without any actual work performed apart from the setup costs.
206-
on_initialize {}: {
205+
// The base weight consumed on processing contracts deletion queue.
206+
on_process_deletion_queue_batch {}: {
207207
Storage::<T>::process_deletion_queue_batch(Weight::MAX)
208208
}
209209

frame/contracts/src/lib.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -380,15 +380,28 @@ pub mod pallet {
380380
T::AccountId: UncheckedFrom<T::Hash>,
381381
T::AccountId: AsRef<[u8]>,
382382
{
383+
fn on_idle(_block: T::BlockNumber, remaining_weight: Weight) -> Weight {
384+
Storage::<T>::process_deletion_queue_batch(remaining_weight)
385+
.saturating_add(T::WeightInfo::on_process_deletion_queue_batch())
386+
}
387+
383388
fn on_initialize(_block: T::BlockNumber) -> Weight {
384-
// We do not want to go above the block limit and rather avoid lazy deletion
385-
// in that case. This should only happen on runtime upgrades.
386-
let weight_limit = T::BlockWeights::get()
387-
.max_block
388-
.saturating_sub(System::<T>::block_weight().total())
389-
.min(T::DeletionWeightLimit::get());
390-
Storage::<T>::process_deletion_queue_batch(weight_limit)
391-
.saturating_add(T::WeightInfo::on_initialize())
389+
// We want to process the deletion_queue in the on_idle hook. Only in the case
390+
// that the queue length has reached its maximal depth, we process it here.
391+
let max_len = T::DeletionQueueDepth::get() as usize;
392+
let queue_len = <DeletionQueue<T>>::decode_len().unwrap_or(0);
393+
if queue_len >= max_len {
394+
// We do not want to go above the block limit and rather avoid lazy deletion
395+
// in that case. This should only happen on runtime upgrades.
396+
let weight_limit = T::BlockWeights::get()
397+
.max_block
398+
.saturating_sub(System::<T>::block_weight().total())
399+
.min(T::DeletionWeightLimit::get());
400+
Storage::<T>::process_deletion_queue_batch(weight_limit)
401+
.saturating_add(T::WeightInfo::on_process_deletion_queue_batch())
402+
} else {
403+
T::WeightInfo::on_process_deletion_queue_batch()
404+
}
392405
}
393406
}
394407

frame/contracts/src/storage.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ where
224224
/// of those keys can be deleted from the deletion queue given the supplied queue length
225225
/// and weight limit.
226226
pub fn deletion_budget(queue_len: usize, weight_limit: Weight) -> (u64, u32) {
227-
let base_weight = T::WeightInfo::on_initialize();
227+
let base_weight = T::WeightInfo::on_process_deletion_queue_batch();
228228
let weight_per_queue_item = T::WeightInfo::on_initialize_per_queue_item(1) -
229229
T::WeightInfo::on_initialize_per_queue_item(0);
230230
let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) -

frame/contracts/src/tests.rs

Lines changed: 72 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::{
2525
wasm::{PrefabWasmModule, ReturnCode as RuntimeReturnCode},
2626
weights::WeightInfo,
2727
BalanceOf, Code, CodeStorage, Config, ContractInfoOf, DefaultAddressGenerator,
28-
DefaultContractAccessWeight, Error, Pallet, Schedule,
28+
DefaultContractAccessWeight, DeletionQueue, Error, Pallet, Schedule,
2929
};
3030
use assert_matches::assert_matches;
3131
use codec::Encode;
@@ -35,7 +35,8 @@ use frame_support::{
3535
parameter_types,
3636
storage::child,
3737
traits::{
38-
BalanceStatus, ConstU32, ConstU64, Contains, Currency, OnInitialize, ReservableCurrency,
38+
BalanceStatus, ConstU32, ConstU64, Contains, Currency, Get, OnIdle, OnInitialize,
39+
ReservableCurrency,
3940
},
4041
weights::{constants::WEIGHT_PER_SECOND, DispatchClass, PostDispatchInfo, Weight},
4142
};
@@ -1610,13 +1611,32 @@ fn lazy_removal_works() {
16101611
assert_matches!(child::get(trie, &[99]), Some(42));
16111612

16121613
// Run the lazy removal
1613-
Contracts::on_initialize(Weight::max_value());
1614+
Contracts::on_idle(System::block_number(), Weight::max_value());
16141615

16151616
// Value should be gone now
16161617
assert_matches!(child::get::<i32>(trie, &[99]), None);
16171618
});
16181619
}
16191620

1621+
#[test]
1622+
fn lazy_removal_on_full_queue_works_on_initialize() {
1623+
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
1624+
// Fill the deletion queue with dummy values, so that on_initialize attempts
1625+
// to clear the queue
1626+
Storage::<Test>::fill_queue_with_dummies();
1627+
1628+
let queue_len_initial = <DeletionQueue<Test>>::decode_len().unwrap_or(0);
1629+
1630+
// Run the lazy removal
1631+
Contracts::on_initialize(System::block_number());
1632+
1633+
let queue_len_after_on_initialize = <DeletionQueue<Test>>::decode_len().unwrap_or(0);
1634+
1635+
// Queue length should be decreased after call of on_initialize()
1636+
assert!(queue_len_initial - queue_len_after_on_initialize > 0);
1637+
});
1638+
}
1639+
16201640
#[test]
16211641
fn lazy_batch_removal_works() {
16221642
let (code, hash) = compile_module::<Test>("self_destruct").unwrap();
@@ -1661,7 +1681,7 @@ fn lazy_batch_removal_works() {
16611681
}
16621682

16631683
// Run single lazy removal
1664-
Contracts::on_initialize(Weight::max_value());
1684+
Contracts::on_idle(System::block_number(), Weight::max_value());
16651685

16661686
// The single lazy removal should have removed all queued tries
16671687
for trie in tries.iter() {
@@ -1761,7 +1781,34 @@ fn lazy_removal_partial_remove_works() {
17611781
}
17621782

17631783
#[test]
1764-
fn lazy_removal_does_no_run_on_full_block() {
1784+
fn lazy_removal_does_no_run_on_full_queue_and_full_block() {
1785+
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
1786+
// Fill up the block which should prevent the lazy storage removal from running.
1787+
System::register_extra_weight_unchecked(
1788+
<Test as system::Config>::BlockWeights::get().max_block,
1789+
DispatchClass::Mandatory,
1790+
);
1791+
1792+
// Fill the deletion queue with dummy values, so that on_initialize attempts
1793+
// to clear the queue
1794+
Storage::<Test>::fill_queue_with_dummies();
1795+
1796+
// Check that on_initialize() tries to perform lazy removal but removes nothing
1797+
// as no more weight is left for that.
1798+
let weight_used = Contracts::on_initialize(System::block_number());
1799+
let base = <<Test as Config>::WeightInfo as WeightInfo>::on_process_deletion_queue_batch();
1800+
assert_eq!(weight_used, base);
1801+
1802+
// Check that the deletion queue is still full after execution of the
1803+
// on_initialize() hook.
1804+
let max_len: u32 = <Test as Config>::DeletionQueueDepth::get();
1805+
let queue_len: u32 = <DeletionQueue<Test>>::decode_len().unwrap_or(0).try_into().unwrap();
1806+
assert_eq!(max_len, queue_len);
1807+
});
1808+
}
1809+
1810+
#[test]
1811+
fn lazy_removal_does_no_run_on_low_remaining_weight() {
17651812
let (code, hash) = compile_module::<Test>("self_destruct").unwrap();
17661813
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
17671814
let min_balance = <Test as Config>::Currency::minimum_balance();
@@ -1779,19 +1826,10 @@ fn lazy_removal_does_no_run_on_full_block() {
17791826

17801827
let addr = Contracts::contract_address(&ALICE, &hash, &[]);
17811828
let info = <ContractInfoOf<Test>>::get(&addr).unwrap();
1782-
let max_keys = 30;
1783-
1784-
// Create some storage items for the contract.
1785-
let vals: Vec<_> = (0..max_keys)
1786-
.map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))
1787-
.collect();
1829+
let trie = &info.child_trie_info();
17881830

17891831
// Put value into the contracts child trie
1790-
for val in &vals {
1791-
Storage::<Test>::write(&info.trie_id, &val.0, Some(val.2.clone()), None, false)
1792-
.unwrap();
1793-
}
1794-
<ContractInfoOf<Test>>::insert(&addr, info.clone());
1832+
child::put(trie, &[99], &42);
17951833

17961834
// Terminate the contract
17971835
assert_ok!(Contracts::call(
@@ -1806,37 +1844,30 @@ fn lazy_removal_does_no_run_on_full_block() {
18061844
// Contract info should be gone
18071845
assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
18081846

1809-
let trie = info.child_trie_info();
1810-
18111847
// But value should be still there as the lazy removal did not run, yet.
1812-
for val in &vals {
1813-
assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), Some(val.1));
1814-
}
1848+
assert_matches!(child::get(trie, &[99]), Some(42));
18151849

1816-
// Fill up the block which should prevent the lazy storage removal from running.
1817-
System::register_extra_weight_unchecked(
1818-
<Test as system::Config>::BlockWeights::get().max_block,
1819-
DispatchClass::Mandatory,
1820-
);
1850+
// Assign a remaining weight which is too low for a successfull deletion of the contract
1851+
let low_remaining_weight =
1852+
<<Test as Config>::WeightInfo as WeightInfo>::on_process_deletion_queue_batch();
18211853

1822-
// Run the lazy removal without any limit so that all keys would be removed if there
1823-
// had been some weight left in the block.
1824-
let weight_used = Contracts::on_initialize(Weight::max_value());
1825-
let base = <<Test as Config>::WeightInfo as WeightInfo>::on_initialize();
1826-
assert_eq!(weight_used, base);
1854+
// Run the lazy removal
1855+
Contracts::on_idle(System::block_number(), low_remaining_weight);
18271856

1828-
// All the keys are still in place
1829-
for val in &vals {
1830-
assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), Some(val.1));
1831-
}
1857+
// Value should still be there, since remaining weight was too low for removal
1858+
assert_matches!(child::get::<i32>(trie, &[99]), Some(42));
18321859

1833-
// Run the lazy removal directly which disregards the block limits
1834-
Storage::<Test>::process_deletion_queue_batch(Weight::max_value());
1860+
// Run the lazy removal while deletion_queue is not full
1861+
Contracts::on_initialize(System::block_number());
18351862

1836-
// Now the keys should be gone
1837-
for val in &vals {
1838-
assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), None);
1839-
}
1863+
// Value should still be there, since deletion_queue was not full
1864+
assert_matches!(child::get::<i32>(trie, &[99]), Some(42));
1865+
1866+
// Run on_idle with max remaining weight, this should remove the value
1867+
Contracts::on_idle(System::block_number(), Weight::max_value());
1868+
1869+
// Value should be gone
1870+
assert_matches!(child::get::<i32>(trie, &[99]), None);
18401871
});
18411872
}
18421873

0 commit comments

Comments
 (0)