1pub mod meter;
21
22use crate::{
23 exec::{AccountIdOf, Key},
24 weights::WeightInfo,
25 BalanceOf, CodeHash, CodeInfo, Config, ContractInfoOf, DeletionQueue, DeletionQueueCounter,
26 Error, TrieId, SENTINEL,
27};
28use alloc::vec::Vec;
29use codec::{Decode, Encode, MaxEncodedLen};
30use core::marker::PhantomData;
31use frame_support::{
32 storage::child::{self, ChildInfo},
33 weights::{Weight, WeightMeter},
34 CloneNoBound, DefaultNoBound,
35};
36use scale_info::TypeInfo;
37use sp_core::Get;
38use sp_io::KillStorageResult;
39use sp_runtime::{
40 traits::{Hash, Saturating, Zero},
41 BoundedBTreeMap, DispatchError, DispatchResult, RuntimeDebug,
42};
43
44use self::meter::Diff;
45
46#[derive(Encode, Decode, CloneNoBound, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
49#[scale_info(skip_type_params(T))]
50pub struct ContractInfo<T: Config> {
51 pub trie_id: TrieId,
53 pub code_hash: CodeHash<T>,
55 storage_bytes: u32,
57 storage_items: u32,
59 pub storage_byte_deposit: BalanceOf<T>,
61 storage_item_deposit: BalanceOf<T>,
63 storage_base_deposit: BalanceOf<T>,
68 delegate_dependencies: BoundedBTreeMap<CodeHash<T>, BalanceOf<T>, T::MaxDelegateDependencies>,
74}
75
76impl<T: Config> ContractInfo<T> {
77 pub fn new(
82 account: &AccountIdOf<T>,
83 nonce: u64,
84 code_hash: CodeHash<T>,
85 ) -> Result<Self, DispatchError> {
86 if <ContractInfoOf<T>>::contains_key(account) {
87 return Err(Error::<T>::DuplicateContract.into())
88 }
89
90 let trie_id = {
91 let buf = (account, nonce).using_encoded(T::Hashing::hash);
92 buf.as_ref()
93 .to_vec()
94 .try_into()
95 .expect("Runtime uses a reasonable hash size. Hence sizeof(T::Hash) <= 128; qed")
96 };
97
98 let contract = Self {
99 trie_id,
100 code_hash,
101 storage_bytes: 0,
102 storage_items: 0,
103 storage_byte_deposit: Zero::zero(),
104 storage_item_deposit: Zero::zero(),
105 storage_base_deposit: Zero::zero(),
106 delegate_dependencies: Default::default(),
107 };
108
109 Ok(contract)
110 }
111
112 pub fn delegate_dependencies_count(&self) -> usize {
114 self.delegate_dependencies.len()
115 }
116
117 pub fn child_trie_info(&self) -> ChildInfo {
119 ChildInfo::new_default(self.trie_id.as_ref())
120 }
121
122 pub fn extra_deposit(&self) -> BalanceOf<T> {
124 self.storage_byte_deposit.saturating_add(self.storage_item_deposit)
125 }
126
127 pub fn total_deposit(&self) -> BalanceOf<T> {
129 self.extra_deposit().saturating_add(self.storage_base_deposit)
130 }
131
132 pub fn storage_base_deposit(&self) -> BalanceOf<T> {
134 self.storage_base_deposit
135 }
136
137 pub fn read(&self, key: &Key<T>) -> Option<Vec<u8>> {
142 child::get_raw(&self.child_trie_info(), key.hash().as_slice())
143 }
144
145 pub fn size(&self, key: &Key<T>) -> Option<u32> {
150 child::len(&self.child_trie_info(), key.hash().as_slice())
151 }
152
153 pub fn write(
161 &self,
162 key: &Key<T>,
163 new_value: Option<Vec<u8>>,
164 storage_meter: Option<&mut meter::NestedMeter<T>>,
165 take: bool,
166 ) -> Result<WriteOutcome, DispatchError> {
167 let hashed_key = key.hash();
168 self.write_raw(&hashed_key, new_value, storage_meter, take)
169 }
170
171 #[cfg(feature = "runtime-benchmarks")]
174 pub fn bench_write_raw(
175 &self,
176 key: &[u8],
177 new_value: Option<Vec<u8>>,
178 take: bool,
179 ) -> Result<WriteOutcome, DispatchError> {
180 self.write_raw(key, new_value, None, take)
181 }
182
183 fn write_raw(
184 &self,
185 key: &[u8],
186 new_value: Option<Vec<u8>>,
187 storage_meter: Option<&mut meter::NestedMeter<T>>,
188 take: bool,
189 ) -> Result<WriteOutcome, DispatchError> {
190 let child_trie_info = &self.child_trie_info();
191 let (old_len, old_value) = if take {
192 let val = child::get_raw(child_trie_info, key);
193 (val.as_ref().map(|v| v.len() as u32), val)
194 } else {
195 (child::len(child_trie_info, key), None)
196 };
197
198 if let Some(storage_meter) = storage_meter {
199 let mut diff = meter::Diff::default();
200 match (old_len, new_value.as_ref().map(|v| v.len() as u32)) {
201 (Some(old_len), Some(new_len)) =>
202 if new_len > old_len {
203 diff.bytes_added = new_len - old_len;
204 } else {
205 diff.bytes_removed = old_len - new_len;
206 },
207 (None, Some(new_len)) => {
208 diff.bytes_added = new_len;
209 diff.items_added = 1;
210 },
211 (Some(old_len), None) => {
212 diff.bytes_removed = old_len;
213 diff.items_removed = 1;
214 },
215 (None, None) => (),
216 }
217 storage_meter.charge(&diff);
218 }
219
220 match &new_value {
221 Some(new_value) => child::put_raw(child_trie_info, key, new_value),
222 None => child::kill(child_trie_info, key),
223 }
224
225 Ok(match (old_len, old_value) {
226 (None, _) => WriteOutcome::New,
227 (Some(old_len), None) => WriteOutcome::Overwritten(old_len),
228 (Some(_), Some(old_value)) => WriteOutcome::Taken(old_value),
229 })
230 }
231
232 pub fn update_base_deposit(&mut self, code_info: &CodeInfo<T>) -> BalanceOf<T> {
237 let info_deposit =
238 Diff { bytes_added: self.encoded_size() as u32, items_added: 1, ..Default::default() }
239 .update_contract::<T>(None)
240 .charge_or_zero();
241
242 let upload_deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_info.deposit());
246
247 let deposit = info_deposit.saturating_add(upload_deposit);
248 self.storage_base_deposit = deposit;
249 deposit
250 }
251
252 pub fn lock_delegate_dependency(
258 &mut self,
259 code_hash: CodeHash<T>,
260 amount: BalanceOf<T>,
261 ) -> DispatchResult {
262 self.delegate_dependencies
263 .try_insert(code_hash, amount)
264 .map_err(|_| Error::<T>::MaxDelegateDependenciesReached)?
265 .map_or(Ok(()), |_| Err(Error::<T>::DelegateDependencyAlreadyExists))
266 .map_err(Into::into)
267 }
268
269 pub fn unlock_delegate_dependency(
274 &mut self,
275 code_hash: &CodeHash<T>,
276 ) -> Result<BalanceOf<T>, DispatchError> {
277 self.delegate_dependencies
278 .remove(code_hash)
279 .ok_or(Error::<T>::DelegateDependencyNotFound.into())
280 }
281
282 pub fn delegate_dependencies(
284 &self,
285 ) -> &BoundedBTreeMap<CodeHash<T>, BalanceOf<T>, T::MaxDelegateDependencies> {
286 &self.delegate_dependencies
287 }
288
289 pub fn queue_trie_for_deletion(&self) {
293 DeletionQueueManager::<T>::load().insert(self.trie_id.clone());
294 }
295
296 pub fn deletion_budget(meter: &WeightMeter) -> (Weight, u32) {
299 let base_weight = T::WeightInfo::on_process_deletion_queue_batch();
300 let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) -
301 T::WeightInfo::on_initialize_per_trie_key(0);
302
303 let key_budget = meter
306 .limit()
307 .saturating_sub(base_weight)
308 .checked_div_per_component(&weight_per_key)
309 .unwrap_or(0) as u32;
310
311 (weight_per_key, key_budget)
312 }
313
314 pub fn process_deletion_queue_batch(meter: &mut WeightMeter) {
316 if meter.try_consume(T::WeightInfo::on_process_deletion_queue_batch()).is_err() {
317 return
318 };
319
320 let mut queue = <DeletionQueueManager<T>>::load();
321 if queue.is_empty() {
322 return;
323 }
324
325 let (weight_per_key, budget) = Self::deletion_budget(&meter);
326 let mut remaining_key_budget = budget;
327 while remaining_key_budget > 0 {
328 let Some(entry) = queue.next() else { break };
329
330 #[allow(deprecated)]
331 let outcome = child::kill_storage(
332 &ChildInfo::new_default(&entry.trie_id),
333 Some(remaining_key_budget),
334 );
335
336 match outcome {
337 KillStorageResult::SomeRemaining(keys_removed) => {
339 remaining_key_budget.saturating_reduce(keys_removed);
340 break
341 },
342 KillStorageResult::AllRemoved(keys_removed) => {
343 entry.remove();
344 remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed.max(1));
346 },
347 };
348 }
349
350 meter.consume(weight_per_key.saturating_mul(u64::from(budget - remaining_key_budget)))
351 }
352
353 pub fn load_code_hash(account: &AccountIdOf<T>) -> Option<CodeHash<T>> {
355 <ContractInfoOf<T>>::get(account).map(|i| i.code_hash)
356 }
357}
358
359#[cfg_attr(any(test, feature = "runtime-benchmarks"), derive(Debug, PartialEq))]
361pub enum WriteOutcome {
362 New,
364 Overwritten(u32),
366 Taken(Vec<u8>),
372}
373
374impl WriteOutcome {
375 pub fn old_len(&self) -> u32 {
378 match self {
379 Self::New => 0,
380 Self::Overwritten(len) => *len,
381 Self::Taken(value) => value.len() as u32,
382 }
383 }
384
385 pub fn old_len_with_sentinel(&self) -> u32 {
393 match self {
394 Self::New => SENTINEL,
395 Self::Overwritten(len) => *len,
396 Self::Taken(value) => value.len() as u32,
397 }
398 }
399}
400
401#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, DefaultNoBound, Clone)]
407#[scale_info(skip_type_params(T))]
408pub struct DeletionQueueManager<T: Config> {
409 insert_counter: u32,
412 delete_counter: u32,
415
416 _phantom: PhantomData<T>,
417}
418
419struct DeletionQueueEntry<'a, T: Config> {
421 trie_id: TrieId,
423
424 queue: &'a mut DeletionQueueManager<T>,
427}
428
429impl<'a, T: Config> DeletionQueueEntry<'a, T> {
430 fn remove(self) {
432 <DeletionQueue<T>>::remove(self.queue.delete_counter);
433 self.queue.delete_counter = self.queue.delete_counter.wrapping_add(1);
434 <DeletionQueueCounter<T>>::set(self.queue.clone());
435 }
436}
437
438impl<T: Config> DeletionQueueManager<T> {
439 fn load() -> Self {
442 <DeletionQueueCounter<T>>::get()
443 }
444
445 fn is_empty(&self) -> bool {
447 self.insert_counter.wrapping_sub(self.delete_counter) == 0
448 }
449
450 fn insert(&mut self, trie_id: TrieId) {
452 <DeletionQueue<T>>::insert(self.insert_counter, trie_id);
453 self.insert_counter = self.insert_counter.wrapping_add(1);
454 <DeletionQueueCounter<T>>::set(self.clone());
455 }
456
457 fn next(&mut self) -> Option<DeletionQueueEntry<T>> {
463 if self.is_empty() {
464 return None
465 }
466
467 let entry = <DeletionQueue<T>>::get(self.delete_counter);
468 entry.map(|trie_id| DeletionQueueEntry { trie_id, queue: self })
469 }
470}
471
472#[cfg(test)]
473impl<T: Config> DeletionQueueManager<T> {
474 pub fn from_test_values(insert_counter: u32, delete_counter: u32) -> Self {
475 Self { insert_counter, delete_counter, _phantom: Default::default() }
476 }
477 pub fn as_test_tuple(&self) -> (u32, u32) {
478 (self.insert_counter, self.delete_counter)
479 }
480}