1use crate::{
21 AccountInfoOf, BalanceOf, BalanceWithDust, Config, DeletionQueue, DeletionQueueCounter, Error,
22 NativeDepositOf, SENTINEL, TrieId,
23 address::AddressMapper,
24 exec::{AccountIdOf, Key},
25 metering::FrameMeter,
26 tracing::if_tracing,
27 weights::WeightInfo,
28};
29use alloc::vec::Vec;
30use codec::{Decode, Encode, MaxEncodedLen};
31use core::marker::PhantomData;
32use frame_support::{
33 CloneNoBound, DebugNoBound, DefaultNoBound,
34 storage::child::{self, ChildInfo},
35 traits::{
36 fungible::Inspect,
37 tokens::{Fortitude, Preservation},
38 },
39 weights::{Weight, WeightMeter},
40};
41use scale_info::TypeInfo;
42use sp_core::{Get, H160};
43use sp_io::KillStorageResult;
44use sp_runtime::{
45 Debug, DispatchError,
46 traits::{Hash, Saturating, Zero},
47};
48
49use crate::metering::Diff;
50
51pub enum AccountIdOrAddress<T: Config> {
52 AccountId(AccountIdOf<T>),
54 Address(H160),
56}
57
58#[derive(
60 DefaultNoBound, Encode, Decode, CloneNoBound, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen,
61)]
62#[scale_info(skip_type_params(T))]
63pub struct AccountInfo<T: Config> {
64 pub account_type: AccountType<T>,
66
67 pub dust: u32,
70}
71
72#[derive(
74 DefaultNoBound, Encode, Decode, CloneNoBound, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen,
75)]
76#[scale_info(skip_type_params(T))]
77pub enum AccountType<T: Config> {
78 Contract(ContractInfo<T>),
80
81 #[default]
83 EOA,
84}
85
86#[derive(Encode, Decode, CloneNoBound, PartialEq, Eq, DebugNoBound, TypeInfo, MaxEncodedLen)]
89#[scale_info(skip_type_params(T))]
90pub struct ContractInfo<T: Config> {
91 pub trie_id: TrieId,
93 pub code_hash: sp_core::H256,
95 pub storage_bytes: u32,
97 pub storage_items: u32,
99 pub storage_byte_deposit: BalanceOf<T>,
101 pub storage_item_deposit: BalanceOf<T>,
103 pub storage_base_deposit: BalanceOf<T>,
108 pub immutable_data_len: u32,
110}
111
112impl<T: Config> From<H160> for AccountIdOrAddress<T> {
113 fn from(address: H160) -> Self {
114 AccountIdOrAddress::Address(address)
115 }
116}
117
118impl<T: Config> AccountIdOrAddress<T> {
119 pub fn address(&self) -> H160 {
120 match self {
121 AccountIdOrAddress::AccountId(id) => {
122 <T::AddressMapper as AddressMapper<T>>::to_address(id)
123 },
124 AccountIdOrAddress::Address(address) => *address,
125 }
126 }
127
128 pub fn account_id(&self) -> AccountIdOf<T> {
129 match self {
130 AccountIdOrAddress::AccountId(id) => id.clone(),
131 AccountIdOrAddress::Address(address) => T::AddressMapper::to_account_id(address),
132 }
133 }
134}
135
136impl<T: Config> From<ContractInfo<T>> for AccountType<T> {
137 fn from(contract_info: ContractInfo<T>) -> Self {
138 AccountType::Contract(contract_info)
139 }
140}
141
142impl<T: Config> AccountInfo<T> {
143 pub fn is_contract(address: &H160) -> bool {
145 let Some(info) = <AccountInfoOf<T>>::get(address) else { return false };
146 matches!(info.account_type, AccountType::Contract(_))
147 }
148
149 pub fn balance_of(account: AccountIdOrAddress<T>) -> BalanceWithDust<BalanceOf<T>> {
151 let info = <AccountInfoOf<T>>::get(account.address()).unwrap_or_default();
152 info.balance(&account.account_id(), Preservation::Preserve)
153 }
154
155 pub fn balance(
157 &self,
158 account: &AccountIdOf<T>,
159 preservation: Preservation,
160 ) -> BalanceWithDust<BalanceOf<T>> {
161 let value = T::Currency::reducible_balance(account, preservation, Fortitude::Polite);
162 BalanceWithDust::new_unchecked::<T>(value, self.dust)
163 }
164
165 pub fn total_balance(account: AccountIdOrAddress<T>) -> BalanceWithDust<BalanceOf<T>> {
167 let value = T::Currency::total_balance(&account.account_id());
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 account_id = T::AddressMapper::to_fallback_account_id(address);
210 if NativeDepositOf::<T>::iter_prefix(&account_id).next().is_some() {
211 return Err(Error::<T>::PendingDepositCleanup.into());
212 }
213
214 let trie_id = {
215 let buf = ("bcontract_trie_v1", address, nonce).using_encoded(T::Hashing::hash);
216 buf.as_ref()
217 .to_vec()
218 .try_into()
219 .expect("Runtime uses a reasonable hash size. Hence sizeof(T::Hash) <= 128; qed")
220 };
221
222 let contract = Self {
223 trie_id,
224 code_hash,
225 storage_bytes: 0,
226 storage_items: 0,
227 storage_byte_deposit: Zero::zero(),
228 storage_item_deposit: Zero::zero(),
229 storage_base_deposit: Zero::zero(),
230 immutable_data_len: 0,
231 };
232
233 Ok(contract)
234 }
235
236 pub fn child_trie_info(&self) -> ChildInfo {
238 ChildInfo::new_default(self.trie_id.as_ref())
239 }
240
241 pub fn extra_deposit(&self) -> BalanceOf<T> {
243 self.storage_byte_deposit.saturating_add(self.storage_item_deposit)
244 }
245
246 pub fn total_deposit(&self) -> BalanceOf<T> {
248 self.extra_deposit().saturating_add(self.storage_base_deposit)
249 }
250
251 pub fn storage_base_deposit(&self) -> BalanceOf<T> {
253 self.storage_base_deposit
254 }
255
256 pub fn read(&self, key: &Key) -> Option<Vec<u8>> {
261 let value = child::get_raw(&self.child_trie_info(), key.hash().as_slice());
262 log::trace!(target: crate::LOG_TARGET, "contract storage: read value {:?} for key {:x?}", value, key);
263 if_tracing(|t| {
264 t.storage_read(key, value.as_deref());
265 });
266 return value;
267 }
268
269 pub fn size(&self, key: &Key) -> Option<u32> {
274 child::len(&self.child_trie_info(), key.hash().as_slice())
275 }
276
277 pub fn write(
285 &self,
286 key: &Key,
287 new_value: Option<Vec<u8>>,
288 frame_meter: Option<&mut FrameMeter<T>>,
289 take: bool,
290 ) -> Result<WriteOutcome, DispatchError> {
291 log::trace!(target: crate::LOG_TARGET, "contract storage: writing value {:?} for key {:x?}", new_value, key);
292 let hashed_key = key.hash();
293 if_tracing(|t| {
294 let old = child::get_raw(&self.child_trie_info(), hashed_key.as_slice());
295 t.storage_write(key, old, new_value.as_deref());
296 });
297
298 self.write_raw(&hashed_key, new_value.as_deref(), frame_meter, take)
299 }
300
301 #[cfg(feature = "runtime-benchmarks")]
304 pub fn bench_write_raw(
305 &self,
306 key: &[u8],
307 new_value: Option<Vec<u8>>,
308 take: bool,
309 ) -> Result<WriteOutcome, DispatchError> {
310 self.write_raw(key, new_value.as_deref(), None, take)
311 }
312
313 fn write_raw(
314 &self,
315 key: &[u8],
316 new_value: Option<&[u8]>,
317 frame_meter: Option<&mut FrameMeter<T>>,
318 take: bool,
319 ) -> Result<WriteOutcome, DispatchError> {
320 let child_trie_info = &self.child_trie_info();
321 let (old_len, old_value) = if take {
322 let val = child::get_raw(child_trie_info, key);
323 (val.as_ref().map(|v| v.len() as u32), val)
324 } else {
325 (child::len(child_trie_info, key), None)
326 };
327
328 if let Some(frame_meter) = frame_meter {
329 let mut diff = Diff::default();
330 let key_len = key.len() as u32;
331 match (old_len, new_value.as_ref().map(|v| v.len() as u32)) {
332 (Some(old_len), Some(new_len)) => {
333 if new_len > old_len {
334 diff.bytes_added = new_len - old_len;
335 } else {
336 diff.bytes_removed = old_len - new_len;
337 }
338 },
339 (None, Some(new_len)) => {
340 diff.bytes_added = new_len.saturating_add(key_len);
341 diff.items_added = 1;
342 },
343 (Some(old_len), None) => {
344 diff.bytes_removed = old_len.saturating_add(key_len);
345 diff.items_removed = 1;
346 },
347 (None, None) => (),
348 }
349 frame_meter.record_contract_storage_changes(&diff)?;
350 }
351
352 match &new_value {
353 Some(new_value) => child::put_raw(child_trie_info, key, new_value),
354 None => child::kill(child_trie_info, key),
355 }
356
357 Ok(match (old_len, old_value) {
358 (None, _) => WriteOutcome::New,
359 (Some(old_len), None) => WriteOutcome::Overwritten(old_len),
360 (Some(_), Some(old_value)) => WriteOutcome::Taken(old_value),
361 })
362 }
363
364 pub fn update_base_deposit(&mut self, code_deposit: BalanceOf<T>) -> BalanceOf<T> {
370 let contract_deposit = {
371 let bytes_added: u32 =
372 (self.encoded_size() as u32).saturating_add(self.immutable_data_len);
373 let items_added: u32 = if self.immutable_data_len == 0 { 1 } else { 2 };
374
375 T::DepositPerByte::get()
376 .saturating_mul(bytes_added.into())
377 .saturating_add(T::DepositPerItem::get().saturating_mul(items_added.into()))
378 };
379
380 let code_deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_deposit);
384
385 let deposit = contract_deposit.saturating_add(code_deposit);
386 self.storage_base_deposit = deposit;
387 deposit
388 }
389
390 pub fn queue_for_deletion(trie_id: TrieId, contract: AccountIdOf<T>) {
396 DeletionQueueManager::<T>::load().insert(DeletionQueueItem::new(trie_id, contract));
397 }
398
399 pub fn deletion_budget(meter: &WeightMeter) -> Weight {
402 meter.limit().saturating_sub(T::WeightInfo::deletion_queue_batch())
403 }
404
405 pub fn process_deletion_queue_batch(meter: &mut WeightMeter) {
408 if meter.try_consume(T::WeightInfo::deletion_queue_batch()).is_err() {
409 return;
410 };
411
412 let mut queue = <DeletionQueueManager<T>>::load();
413 if queue.is_empty() {
414 return;
415 }
416
417 let weight_per_entry = T::WeightInfo::deletion_queue_per_entry()
418 .saturating_sub(T::WeightInfo::deletion_queue_batch());
419 let weight_per_native_key = T::WeightInfo::deletion_queue_per_native_deposit_key(1)
420 .saturating_sub(T::WeightInfo::deletion_queue_per_native_deposit_key(0));
421 let weight_per_trie_key = T::WeightInfo::deletion_queue_per_trie_key(1)
422 .saturating_sub(T::WeightInfo::deletion_queue_per_trie_key(0));
423
424 let budget = Self::deletion_budget(&meter);
425 let mut remaining = budget;
426
427 let key_budget_for = |remaining: Weight, w: Weight| -> u32 {
428 remaining.checked_div_per_component(&w).unwrap_or(0).min(u32::MAX as u64) as u32
431 };
432
433 loop {
434 let Some(entry) = queue.next() else { break };
435
436 let Some(after_entry) = remaining.checked_sub(&weight_per_entry) else { break };
438 remaining = after_entry;
439
440 let key_budget = key_budget_for(remaining, weight_per_native_key);
442 if key_budget == 0 {
443 break;
444 }
445 let result =
446 NativeDepositOf::<T>::clear_prefix(&entry.value.account_id, key_budget, None);
447 remaining = remaining
448 .saturating_sub(weight_per_native_key.saturating_mul(u64::from(result.unique)));
449 if result.maybe_cursor.is_some() {
450 break;
451 }
452
453 let key_budget = key_budget_for(remaining, weight_per_trie_key);
455 if key_budget == 0 {
456 break;
457 }
458 #[allow(deprecated)]
459 let outcome = child::kill_storage(
460 &ChildInfo::new_default(&entry.value.trie_id),
461 Some(key_budget),
462 );
463 match outcome {
464 KillStorageResult::SomeRemaining(keys_removed) => {
465 remaining = remaining
466 .saturating_sub(weight_per_trie_key.saturating_mul(keys_removed.into()));
467 break;
468 },
469 KillStorageResult::AllRemoved(keys_removed) => {
470 remaining = remaining.saturating_sub(
471 weight_per_trie_key.saturating_mul(u64::from(keys_removed)),
472 );
473 entry.remove();
474 },
475 };
476 }
477
478 meter.consume(budget.saturating_sub(remaining));
479 }
480
481 pub fn load_code_hash(account: &AccountIdOf<T>) -> Option<sp_core::H256> {
483 <AccountInfo<T>>::load_contract(&T::AddressMapper::to_address(account)).map(|i| i.code_hash)
484 }
485
486 pub fn immutable_data_len(&self) -> u32 {
488 self.immutable_data_len
489 }
490
491 pub fn set_immutable_data_len(&mut self, immutable_data_len: u32) {
493 self.immutable_data_len = immutable_data_len;
494 }
495}
496
497#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
499pub enum WriteOutcome {
500 New,
502 Overwritten(u32),
504 Taken(Vec<u8>),
510}
511
512impl WriteOutcome {
513 pub fn old_len(&self) -> u32 {
516 match self {
517 Self::New => 0,
518 Self::Overwritten(len) => *len,
519 Self::Taken(value) => value.len() as u32,
520 }
521 }
522
523 pub fn old_len_with_sentinel(&self) -> u32 {
531 match self {
532 Self::New => SENTINEL,
533 Self::Overwritten(len) => *len,
534 Self::Taken(value) => value.len() as u32,
535 }
536 }
537}
538
539#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, DefaultNoBound, Clone)]
545#[scale_info(skip_type_params(T))]
546pub struct DeletionQueueManager<T: Config> {
547 insert_counter: u32,
550 delete_counter: u32,
553
554 _phantom: PhantomData<T>,
555}
556
557#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, CloneNoBound, DebugNoBound, PartialEq, Eq)]
563#[scale_info(skip_type_params(T))]
564pub struct DeletionQueueItem<T: Config> {
565 pub trie_id: TrieId,
567 pub account_id: AccountIdOf<T>,
569}
570
571impl<T: Config> DeletionQueueItem<T> {
572 pub fn new(trie_id: TrieId, account_id: AccountIdOf<T>) -> Self {
573 Self { trie_id, account_id }
574 }
575}
576
577struct DeletionQueueEntry<'a, T: Config> {
579 value: DeletionQueueItem<T>,
581
582 queue: &'a mut DeletionQueueManager<T>,
585}
586
587impl<'a, T: Config> DeletionQueueEntry<'a, T> {
588 fn remove(self) {
590 <DeletionQueue<T>>::remove(self.queue.delete_counter);
591 self.queue.delete_counter = self.queue.delete_counter.wrapping_add(1);
592 <DeletionQueueCounter<T>>::set(self.queue.clone());
593 }
594}
595
596impl<T: Config> DeletionQueueManager<T> {
597 fn load() -> Self {
600 <DeletionQueueCounter<T>>::get()
601 }
602
603 fn is_empty(&self) -> bool {
605 self.insert_counter.wrapping_sub(self.delete_counter) == 0
606 }
607
608 fn insert(&mut self, value: DeletionQueueItem<T>) {
610 <DeletionQueue<T>>::insert(self.insert_counter, value);
611 self.insert_counter = self.insert_counter.wrapping_add(1);
612 <DeletionQueueCounter<T>>::set(self.clone());
613 }
614
615 fn next(&mut self) -> Option<DeletionQueueEntry<'_, T>> {
621 if self.is_empty() {
622 return None;
623 }
624
625 let entry = <DeletionQueue<T>>::get(self.delete_counter);
626 entry.map(|value| DeletionQueueEntry { value, queue: self })
627 }
628}
629
630#[cfg(test)]
631impl<T: Config> DeletionQueueManager<T> {
632 pub fn from_test_values(insert_counter: u32, delete_counter: u32) -> Self {
633 Self { insert_counter, delete_counter, _phantom: Default::default() }
634 }
635 pub fn as_test_tuple(&self) -> (u32, u32) {
636 (self.insert_counter, self.delete_counter)
637 }
638}