1pub mod meter;
21
22use crate::{
23 address::AddressMapper,
24 exec::{AccountIdOf, Key},
25 storage::meter::Diff,
26 tracing::if_tracing,
27 weights::WeightInfo,
28 AccountInfoOf, BalanceOf, BalanceWithDust, Config, DeletionQueue, DeletionQueueCounter, Error,
29 TrieId, SENTINEL,
30};
31use alloc::vec::Vec;
32use codec::{Decode, Encode, MaxEncodedLen};
33use core::marker::PhantomData;
34use frame_support::{
35 storage::child::{self, ChildInfo},
36 weights::{Weight, WeightMeter},
37 CloneNoBound, DebugNoBound, DefaultNoBound,
38};
39use scale_info::TypeInfo;
40use sp_core::{Get, H160};
41use sp_io::KillStorageResult;
42use sp_runtime::{
43 traits::{Hash, Saturating, Zero},
44 DispatchError, RuntimeDebug,
45};
46
47pub enum AccountIdOrAddress<T: Config> {
48 AccountId(AccountIdOf<T>),
50 Address(H160),
52}
53
54#[derive(
56 DefaultNoBound,
57 Encode,
58 Decode,
59 CloneNoBound,
60 PartialEq,
61 Eq,
62 RuntimeDebug,
63 TypeInfo,
64 MaxEncodedLen,
65)]
66#[scale_info(skip_type_params(T))]
67pub struct AccountInfo<T: Config> {
68 pub account_type: AccountType<T>,
70
71 pub dust: u32,
74}
75
76#[derive(
78 DefaultNoBound,
79 Encode,
80 Decode,
81 CloneNoBound,
82 PartialEq,
83 Eq,
84 RuntimeDebug,
85 TypeInfo,
86 MaxEncodedLen,
87)]
88#[scale_info(skip_type_params(T))]
89pub enum AccountType<T: Config> {
90 Contract(ContractInfo<T>),
92
93 #[default]
95 EOA,
96}
97
98#[derive(Encode, Decode, CloneNoBound, PartialEq, Eq, DebugNoBound, TypeInfo, MaxEncodedLen)]
101#[scale_info(skip_type_params(T))]
102pub struct ContractInfo<T: Config> {
103 pub trie_id: TrieId,
105 pub code_hash: sp_core::H256,
107 storage_bytes: u32,
109 storage_items: u32,
111 pub storage_byte_deposit: BalanceOf<T>,
113 storage_item_deposit: BalanceOf<T>,
115 storage_base_deposit: BalanceOf<T>,
120 immutable_data_len: u32,
122}
123
124impl<T: Config> From<H160> for AccountIdOrAddress<T> {
125 fn from(address: H160) -> Self {
126 AccountIdOrAddress::Address(address)
127 }
128}
129
130impl<T: Config> AccountIdOrAddress<T> {
131 pub fn address(&self) -> H160 {
132 match self {
133 AccountIdOrAddress::AccountId(id) =>
134 <T::AddressMapper as AddressMapper<T>>::to_address(id),
135 AccountIdOrAddress::Address(address) => *address,
136 }
137 }
138
139 pub fn account_id(&self) -> AccountIdOf<T> {
140 match self {
141 AccountIdOrAddress::AccountId(id) => id.clone(),
142 AccountIdOrAddress::Address(address) => T::AddressMapper::to_account_id(address),
143 }
144 }
145}
146
147impl<T: Config> From<ContractInfo<T>> for AccountType<T> {
148 fn from(contract_info: ContractInfo<T>) -> Self {
149 AccountType::Contract(contract_info)
150 }
151}
152
153impl<T: Config> AccountInfo<T> {
154 fn is_contract(address: &H160) -> bool {
156 let Some(info) = <AccountInfoOf<T>>::get(address) else { return false };
157 matches!(info.account_type, AccountType::Contract(_))
158 }
159
160 pub fn balance(account: AccountIdOrAddress<T>) -> BalanceWithDust<BalanceOf<T>> {
162 use frame_support::traits::{
163 fungible::Inspect,
164 tokens::{Fortitude::Polite, Preservation::Preserve},
165 };
166
167 let value = T::Currency::reducible_balance(&account.account_id(), Preserve, Polite);
168 let dust = <AccountInfoOf<T>>::get(account.address()).map(|a| a.dust).unwrap_or_default();
169 BalanceWithDust::new_unchecked::<T>(value, dust)
170 }
171
172 pub fn load_contract(address: &H160) -> Option<ContractInfo<T>> {
174 let Some(info) = <AccountInfoOf<T>>::get(address) else { return None };
175 let AccountType::Contract(contract_info) = info.account_type else { return None };
176 Some(contract_info)
177 }
178
179 pub fn insert_contract(address: &H160, contract: ContractInfo<T>) {
181 AccountInfoOf::<T>::mutate(address, |account| {
182 if let Some(account) = account {
183 account.account_type = contract.clone().into();
184 } else {
185 *account = Some(AccountInfo { account_type: contract.clone().into(), dust: 0 });
186 }
187 });
188 }
189}
190
191impl<T: Config> ContractInfo<T> {
192 pub fn new(
197 address: &H160,
198 nonce: T::Nonce,
199 code_hash: sp_core::H256,
200 ) -> Result<Self, DispatchError> {
201 if <AccountInfo<T>>::is_contract(address) {
202 return Err(Error::<T>::DuplicateContract.into());
203 }
204
205 let trie_id = {
206 let buf = ("bcontract_trie_v1", address, nonce).using_encoded(T::Hashing::hash);
207 buf.as_ref()
208 .to_vec()
209 .try_into()
210 .expect("Runtime uses a reasonable hash size. Hence sizeof(T::Hash) <= 128; qed")
211 };
212
213 let contract = Self {
214 trie_id,
215 code_hash,
216 storage_bytes: 0,
217 storage_items: 0,
218 storage_byte_deposit: Zero::zero(),
219 storage_item_deposit: Zero::zero(),
220 storage_base_deposit: Zero::zero(),
221 immutable_data_len: 0,
222 };
223
224 Ok(contract)
225 }
226
227 pub fn child_trie_info(&self) -> ChildInfo {
229 ChildInfo::new_default(self.trie_id.as_ref())
230 }
231
232 pub fn extra_deposit(&self) -> BalanceOf<T> {
234 self.storage_byte_deposit.saturating_add(self.storage_item_deposit)
235 }
236
237 pub fn total_deposit(&self) -> BalanceOf<T> {
239 self.extra_deposit().saturating_add(self.storage_base_deposit)
240 }
241
242 pub fn storage_base_deposit(&self) -> BalanceOf<T> {
244 self.storage_base_deposit
245 }
246
247 pub fn read(&self, key: &Key) -> Option<Vec<u8>> {
252 let value = child::get_raw(&self.child_trie_info(), key.hash().as_slice());
253 if_tracing(|t| {
254 t.storage_read(key, value.as_deref());
255 });
256 return value
257 }
258
259 pub fn size(&self, key: &Key) -> Option<u32> {
264 child::len(&self.child_trie_info(), key.hash().as_slice())
265 }
266
267 pub fn write(
275 &self,
276 key: &Key,
277 new_value: Option<Vec<u8>>,
278 storage_meter: Option<&mut meter::NestedMeter<T>>,
279 take: bool,
280 ) -> Result<WriteOutcome, DispatchError> {
281 let hashed_key = key.hash();
282 if_tracing(|t| {
283 let old = child::get_raw(&self.child_trie_info(), hashed_key.as_slice());
284 t.storage_write(key, old, new_value.as_deref());
285 });
286
287 self.write_raw(&hashed_key, new_value.as_deref(), storage_meter, take)
288 }
289
290 #[cfg(feature = "runtime-benchmarks")]
293 pub fn bench_write_raw(
294 &self,
295 key: &[u8],
296 new_value: Option<Vec<u8>>,
297 take: bool,
298 ) -> Result<WriteOutcome, DispatchError> {
299 self.write_raw(key, new_value.as_deref(), None, take)
300 }
301
302 fn write_raw(
303 &self,
304 key: &[u8],
305 new_value: Option<&[u8]>,
306 storage_meter: Option<&mut meter::NestedMeter<T>>,
307 take: bool,
308 ) -> Result<WriteOutcome, DispatchError> {
309 let child_trie_info = &self.child_trie_info();
310 let (old_len, old_value) = if take {
311 let val = child::get_raw(child_trie_info, key);
312 (val.as_ref().map(|v| v.len() as u32), val)
313 } else {
314 (child::len(child_trie_info, key), None)
315 };
316
317 if let Some(storage_meter) = storage_meter {
318 let mut diff = meter::Diff::default();
319 let key_len = key.len() as u32;
320 match (old_len, new_value.as_ref().map(|v| v.len() as u32)) {
321 (Some(old_len), Some(new_len)) =>
322 if new_len > old_len {
323 diff.bytes_added = new_len - old_len;
324 } else {
325 diff.bytes_removed = old_len - new_len;
326 },
327 (None, Some(new_len)) => {
328 diff.bytes_added = new_len.saturating_add(key_len);
329 diff.items_added = 1;
330 },
331 (Some(old_len), None) => {
332 diff.bytes_removed = old_len.saturating_add(key_len);
333 diff.items_removed = 1;
334 },
335 (None, None) => (),
336 }
337 storage_meter.charge(&diff);
338 }
339
340 match &new_value {
341 Some(new_value) => child::put_raw(child_trie_info, key, new_value),
342 None => child::kill(child_trie_info, key),
343 }
344
345 Ok(match (old_len, old_value) {
346 (None, _) => WriteOutcome::New,
347 (Some(old_len), None) => WriteOutcome::Overwritten(old_len),
348 (Some(_), Some(old_value)) => WriteOutcome::Taken(old_value),
349 })
350 }
351
352 pub fn update_base_deposit(&mut self, code_deposit: BalanceOf<T>) -> BalanceOf<T> {
358 let contract_deposit = Diff {
359 bytes_added: (self.encoded_size() as u32).saturating_add(self.immutable_data_len),
360 items_added: if self.immutable_data_len == 0 { 1 } else { 2 },
361 ..Default::default()
362 }
363 .update_contract::<T>(None)
364 .charge_or_zero();
365
366 let code_deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_deposit);
370
371 let deposit = contract_deposit.saturating_add(code_deposit);
372 self.storage_base_deposit = deposit;
373 deposit
374 }
375
376 pub fn queue_trie_for_deletion(&self) {
380 DeletionQueueManager::<T>::load().insert(self.trie_id.clone());
381 }
382
383 pub fn deletion_budget(meter: &WeightMeter) -> (Weight, u32) {
386 let base_weight = T::WeightInfo::on_process_deletion_queue_batch();
387 let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) -
388 T::WeightInfo::on_initialize_per_trie_key(0);
389
390 let key_budget = meter
393 .limit()
394 .saturating_sub(base_weight)
395 .checked_div_per_component(&weight_per_key)
396 .unwrap_or(0) as u32;
397
398 (weight_per_key, key_budget)
399 }
400
401 pub fn process_deletion_queue_batch(meter: &mut WeightMeter) {
403 if meter.try_consume(T::WeightInfo::on_process_deletion_queue_batch()).is_err() {
404 return
405 };
406
407 let mut queue = <DeletionQueueManager<T>>::load();
408 if queue.is_empty() {
409 return;
410 }
411
412 let (weight_per_key, budget) = Self::deletion_budget(&meter);
413 let mut remaining_key_budget = budget;
414 while remaining_key_budget > 0 {
415 let Some(entry) = queue.next() else { break };
416
417 #[allow(deprecated)]
418 let outcome = child::kill_storage(
419 &ChildInfo::new_default(&entry.trie_id),
420 Some(remaining_key_budget),
421 );
422
423 match outcome {
424 KillStorageResult::SomeRemaining(keys_removed) => {
426 remaining_key_budget.saturating_reduce(keys_removed);
427 break
428 },
429 KillStorageResult::AllRemoved(keys_removed) => {
430 entry.remove();
431 remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed.max(1));
433 },
434 };
435 }
436
437 meter.consume(weight_per_key.saturating_mul(u64::from(budget - remaining_key_budget)))
438 }
439
440 pub fn load_code_hash(account: &AccountIdOf<T>) -> Option<sp_core::H256> {
442 <AccountInfo<T>>::load_contract(&T::AddressMapper::to_address(account)).map(|i| i.code_hash)
443 }
444
445 pub fn immutable_data_len(&self) -> u32 {
447 self.immutable_data_len
448 }
449
450 pub fn set_immutable_data_len(&mut self, immutable_data_len: u32) {
452 self.immutable_data_len = immutable_data_len;
453 }
454}
455
456#[cfg_attr(any(test, feature = "runtime-benchmarks"), derive(Debug, PartialEq))]
458pub enum WriteOutcome {
459 New,
461 Overwritten(u32),
463 Taken(Vec<u8>),
469}
470
471impl WriteOutcome {
472 pub fn old_len(&self) -> u32 {
475 match self {
476 Self::New => 0,
477 Self::Overwritten(len) => *len,
478 Self::Taken(value) => value.len() as u32,
479 }
480 }
481
482 pub fn old_len_with_sentinel(&self) -> u32 {
490 match self {
491 Self::New => SENTINEL,
492 Self::Overwritten(len) => *len,
493 Self::Taken(value) => value.len() as u32,
494 }
495 }
496}
497
498#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, DefaultNoBound, Clone)]
504#[scale_info(skip_type_params(T))]
505pub struct DeletionQueueManager<T: Config> {
506 insert_counter: u32,
509 delete_counter: u32,
512
513 _phantom: PhantomData<T>,
514}
515
516struct DeletionQueueEntry<'a, T: Config> {
518 trie_id: TrieId,
520
521 queue: &'a mut DeletionQueueManager<T>,
524}
525
526impl<'a, T: Config> DeletionQueueEntry<'a, T> {
527 fn remove(self) {
529 <DeletionQueue<T>>::remove(self.queue.delete_counter);
530 self.queue.delete_counter = self.queue.delete_counter.wrapping_add(1);
531 <DeletionQueueCounter<T>>::set(self.queue.clone());
532 }
533}
534
535impl<T: Config> DeletionQueueManager<T> {
536 fn load() -> Self {
539 <DeletionQueueCounter<T>>::get()
540 }
541
542 fn is_empty(&self) -> bool {
544 self.insert_counter.wrapping_sub(self.delete_counter) == 0
545 }
546
547 fn insert(&mut self, trie_id: TrieId) {
549 <DeletionQueue<T>>::insert(self.insert_counter, trie_id);
550 self.insert_counter = self.insert_counter.wrapping_add(1);
551 <DeletionQueueCounter<T>>::set(self.clone());
552 }
553
554 fn next(&mut self) -> Option<DeletionQueueEntry<T>> {
560 if self.is_empty() {
561 return None
562 }
563
564 let entry = <DeletionQueue<T>>::get(self.delete_counter);
565 entry.map(|trie_id| DeletionQueueEntry { trie_id, queue: self })
566 }
567}
568
569#[cfg(test)]
570impl<T: Config> DeletionQueueManager<T> {
571 pub fn from_test_values(insert_counter: u32, delete_counter: u32) -> Self {
572 Self { insert_counter, delete_counter, _phantom: Default::default() }
573 }
574 pub fn as_test_tuple(&self) -> (u32, u32) {
575 (self.insert_counter, self.delete_counter)
576 }
577}