1use crate::{
21 address::AddressMapper,
22 exec::{AccountIdOf, Key},
23 metering::FrameMeter,
24 tracing::if_tracing,
25 weights::WeightInfo,
26 AccountInfoOf, BalanceOf, BalanceWithDust, Config, DeletionQueue, DeletionQueueCounter, Error,
27 TrieId, SENTINEL,
28};
29use alloc::vec::Vec;
30use codec::{Decode, Encode, MaxEncodedLen};
31use core::marker::PhantomData;
32use frame_support::{
33 storage::child::{self, ChildInfo},
34 traits::{
35 fungible::Inspect,
36 tokens::{Fortitude, Preservation},
37 },
38 weights::{Weight, WeightMeter},
39 CloneNoBound, DebugNoBound, DefaultNoBound,
40};
41use scale_info::TypeInfo;
42use sp_core::{Get, H160};
43use sp_io::KillStorageResult;
44use sp_runtime::{
45 traits::{Hash, Saturating, Zero},
46 DispatchError, RuntimeDebug,
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,
61 Encode,
62 Decode,
63 CloneNoBound,
64 PartialEq,
65 Eq,
66 RuntimeDebug,
67 TypeInfo,
68 MaxEncodedLen,
69)]
70#[scale_info(skip_type_params(T))]
71pub struct AccountInfo<T: Config> {
72 pub account_type: AccountType<T>,
74
75 pub dust: u32,
78}
79
80#[derive(
82 DefaultNoBound,
83 Encode,
84 Decode,
85 CloneNoBound,
86 PartialEq,
87 Eq,
88 RuntimeDebug,
89 TypeInfo,
90 MaxEncodedLen,
91)]
92#[scale_info(skip_type_params(T))]
93pub enum AccountType<T: Config> {
94 Contract(ContractInfo<T>),
96
97 #[default]
99 EOA,
100}
101
102#[derive(Encode, Decode, CloneNoBound, PartialEq, Eq, DebugNoBound, TypeInfo, MaxEncodedLen)]
105#[scale_info(skip_type_params(T))]
106pub struct ContractInfo<T: Config> {
107 pub trie_id: TrieId,
109 pub code_hash: sp_core::H256,
111 pub storage_bytes: u32,
113 pub storage_items: u32,
115 pub storage_byte_deposit: BalanceOf<T>,
117 pub storage_item_deposit: BalanceOf<T>,
119 pub storage_base_deposit: BalanceOf<T>,
124 pub immutable_data_len: u32,
126}
127
128impl<T: Config> From<H160> for AccountIdOrAddress<T> {
129 fn from(address: H160) -> Self {
130 AccountIdOrAddress::Address(address)
131 }
132}
133
134impl<T: Config> AccountIdOrAddress<T> {
135 pub fn address(&self) -> H160 {
136 match self {
137 AccountIdOrAddress::AccountId(id) =>
138 <T::AddressMapper as AddressMapper<T>>::to_address(id),
139 AccountIdOrAddress::Address(address) => *address,
140 }
141 }
142
143 pub fn account_id(&self) -> AccountIdOf<T> {
144 match self {
145 AccountIdOrAddress::AccountId(id) => id.clone(),
146 AccountIdOrAddress::Address(address) => T::AddressMapper::to_account_id(address),
147 }
148 }
149}
150
151impl<T: Config> From<ContractInfo<T>> for AccountType<T> {
152 fn from(contract_info: ContractInfo<T>) -> Self {
153 AccountType::Contract(contract_info)
154 }
155}
156
157impl<T: Config> AccountInfo<T> {
158 pub fn is_contract(address: &H160) -> bool {
160 let Some(info) = <AccountInfoOf<T>>::get(address) else { return false };
161 matches!(info.account_type, AccountType::Contract(_))
162 }
163
164 pub fn balance_of(account: AccountIdOrAddress<T>) -> BalanceWithDust<BalanceOf<T>> {
166 let info = <AccountInfoOf<T>>::get(account.address()).unwrap_or_default();
167 info.balance(&account.account_id(), Preservation::Preserve)
168 }
169
170 pub fn balance(
172 &self,
173 account: &AccountIdOf<T>,
174 preservation: Preservation,
175 ) -> BalanceWithDust<BalanceOf<T>> {
176 let value = T::Currency::reducible_balance(account, preservation, Fortitude::Polite);
177 BalanceWithDust::new_unchecked::<T>(value, self.dust)
178 }
179
180 pub fn total_balance(account: AccountIdOrAddress<T>) -> BalanceWithDust<BalanceOf<T>> {
182 let value = T::Currency::total_balance(&account.account_id());
183 let dust = <AccountInfoOf<T>>::get(account.address()).map(|a| a.dust).unwrap_or_default();
184 BalanceWithDust::new_unchecked::<T>(value, dust)
185 }
186
187 pub fn load_contract(address: &H160) -> Option<ContractInfo<T>> {
189 let Some(info) = <AccountInfoOf<T>>::get(address) else { return None };
190 let AccountType::Contract(contract_info) = info.account_type else { return None };
191 Some(contract_info)
192 }
193
194 pub fn insert_contract(address: &H160, contract: ContractInfo<T>) {
196 AccountInfoOf::<T>::mutate(address, |account| {
197 if let Some(account) = account {
198 account.account_type = contract.clone().into();
199 } else {
200 *account = Some(AccountInfo { account_type: contract.clone().into(), dust: 0 });
201 }
202 });
203 }
204}
205
206impl<T: Config> ContractInfo<T> {
207 pub fn new(
212 address: &H160,
213 nonce: T::Nonce,
214 code_hash: sp_core::H256,
215 ) -> Result<Self, DispatchError> {
216 if <AccountInfo<T>>::is_contract(address) {
217 return Err(Error::<T>::DuplicateContract.into());
218 }
219
220 let trie_id = {
221 let buf = ("bcontract_trie_v1", address, nonce).using_encoded(T::Hashing::hash);
222 buf.as_ref()
223 .to_vec()
224 .try_into()
225 .expect("Runtime uses a reasonable hash size. Hence sizeof(T::Hash) <= 128; qed")
226 };
227
228 let contract = Self {
229 trie_id,
230 code_hash,
231 storage_bytes: 0,
232 storage_items: 0,
233 storage_byte_deposit: Zero::zero(),
234 storage_item_deposit: Zero::zero(),
235 storage_base_deposit: Zero::zero(),
236 immutable_data_len: 0,
237 };
238
239 Ok(contract)
240 }
241
242 pub fn child_trie_info(&self) -> ChildInfo {
244 ChildInfo::new_default(self.trie_id.as_ref())
245 }
246
247 pub fn extra_deposit(&self) -> BalanceOf<T> {
249 self.storage_byte_deposit.saturating_add(self.storage_item_deposit)
250 }
251
252 pub fn total_deposit(&self) -> BalanceOf<T> {
254 self.extra_deposit().saturating_add(self.storage_base_deposit)
255 }
256
257 pub fn storage_base_deposit(&self) -> BalanceOf<T> {
259 self.storage_base_deposit
260 }
261
262 pub fn read(&self, key: &Key) -> Option<Vec<u8>> {
267 let value = child::get_raw(&self.child_trie_info(), key.hash().as_slice());
268 log::trace!(target: crate::LOG_TARGET, "contract storage: read value {:?} for key {:x?}", value, key);
269 if_tracing(|t| {
270 t.storage_read(key, value.as_deref());
271 });
272 return value
273 }
274
275 pub fn size(&self, key: &Key) -> Option<u32> {
280 child::len(&self.child_trie_info(), key.hash().as_slice())
281 }
282
283 pub fn write(
291 &self,
292 key: &Key,
293 new_value: Option<Vec<u8>>,
294 frame_meter: Option<&mut FrameMeter<T>>,
295 take: bool,
296 ) -> Result<WriteOutcome, DispatchError> {
297 log::trace!(target: crate::LOG_TARGET, "contract storage: writing value {:?} for key {:x?}", new_value, key);
298 let hashed_key = key.hash();
299 if_tracing(|t| {
300 let old = child::get_raw(&self.child_trie_info(), hashed_key.as_slice());
301 t.storage_write(key, old, new_value.as_deref());
302 });
303
304 self.write_raw(&hashed_key, new_value.as_deref(), frame_meter, take)
305 }
306
307 #[cfg(feature = "runtime-benchmarks")]
310 pub fn bench_write_raw(
311 &self,
312 key: &[u8],
313 new_value: Option<Vec<u8>>,
314 take: bool,
315 ) -> Result<WriteOutcome, DispatchError> {
316 self.write_raw(key, new_value.as_deref(), None, take)
317 }
318
319 fn write_raw(
320 &self,
321 key: &[u8],
322 new_value: Option<&[u8]>,
323 frame_meter: Option<&mut FrameMeter<T>>,
324 take: bool,
325 ) -> Result<WriteOutcome, DispatchError> {
326 let child_trie_info = &self.child_trie_info();
327 let (old_len, old_value) = if take {
328 let val = child::get_raw(child_trie_info, key);
329 (val.as_ref().map(|v| v.len() as u32), val)
330 } else {
331 (child::len(child_trie_info, key), None)
332 };
333
334 if let Some(frame_meter) = frame_meter {
335 let mut diff = Diff::default();
336 let key_len = key.len() as u32;
337 match (old_len, new_value.as_ref().map(|v| v.len() as u32)) {
338 (Some(old_len), Some(new_len)) =>
339 if new_len > old_len {
340 diff.bytes_added = new_len - old_len;
341 } else {
342 diff.bytes_removed = old_len - new_len;
343 },
344 (None, Some(new_len)) => {
345 diff.bytes_added = new_len.saturating_add(key_len);
346 diff.items_added = 1;
347 },
348 (Some(old_len), None) => {
349 diff.bytes_removed = old_len.saturating_add(key_len);
350 diff.items_removed = 1;
351 },
352 (None, None) => (),
353 }
354 frame_meter.record_contract_storage_changes(&diff)?;
355 }
356
357 match &new_value {
358 Some(new_value) => child::put_raw(child_trie_info, key, new_value),
359 None => child::kill(child_trie_info, key),
360 }
361
362 Ok(match (old_len, old_value) {
363 (None, _) => WriteOutcome::New,
364 (Some(old_len), None) => WriteOutcome::Overwritten(old_len),
365 (Some(_), Some(old_value)) => WriteOutcome::Taken(old_value),
366 })
367 }
368
369 pub fn update_base_deposit(&mut self, code_deposit: BalanceOf<T>) -> BalanceOf<T> {
375 let contract_deposit = {
376 let bytes_added: u32 =
377 (self.encoded_size() as u32).saturating_add(self.immutable_data_len);
378 let items_added: u32 = if self.immutable_data_len == 0 { 1 } else { 2 };
379
380 T::DepositPerByte::get()
381 .saturating_mul(bytes_added.into())
382 .saturating_add(T::DepositPerItem::get().saturating_mul(items_added.into()))
383 };
384
385 let code_deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_deposit);
389
390 let deposit = contract_deposit.saturating_add(code_deposit);
391 self.storage_base_deposit = deposit;
392 deposit
393 }
394
395 pub fn queue_trie_for_deletion(trie_id: TrieId) {
399 DeletionQueueManager::<T>::load().insert(trie_id);
400 }
401
402 pub fn deletion_budget(meter: &WeightMeter) -> (Weight, u32) {
405 let base_weight = T::WeightInfo::on_process_deletion_queue_batch();
406 let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) -
407 T::WeightInfo::on_initialize_per_trie_key(0);
408
409 let key_budget = meter
412 .limit()
413 .saturating_sub(base_weight)
414 .checked_div_per_component(&weight_per_key)
415 .unwrap_or(0) as u32;
416
417 (weight_per_key, key_budget)
418 }
419
420 pub fn process_deletion_queue_batch(meter: &mut WeightMeter) {
422 if meter.try_consume(T::WeightInfo::on_process_deletion_queue_batch()).is_err() {
423 return
424 };
425
426 let mut queue = <DeletionQueueManager<T>>::load();
427 if queue.is_empty() {
428 return;
429 }
430
431 let (weight_per_key, budget) = Self::deletion_budget(&meter);
432 let mut remaining_key_budget = budget;
433 while remaining_key_budget > 0 {
434 let Some(entry) = queue.next() else { break };
435
436 #[allow(deprecated)]
437 let outcome = child::kill_storage(
438 &ChildInfo::new_default(&entry.trie_id),
439 Some(remaining_key_budget),
440 );
441
442 match outcome {
443 KillStorageResult::SomeRemaining(keys_removed) => {
445 remaining_key_budget.saturating_reduce(keys_removed);
446 break
447 },
448 KillStorageResult::AllRemoved(keys_removed) => {
449 entry.remove();
450 remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed.max(1));
452 },
453 };
454 }
455
456 meter.consume(weight_per_key.saturating_mul(u64::from(budget - remaining_key_budget)))
457 }
458
459 pub fn load_code_hash(account: &AccountIdOf<T>) -> Option<sp_core::H256> {
461 <AccountInfo<T>>::load_contract(&T::AddressMapper::to_address(account)).map(|i| i.code_hash)
462 }
463
464 pub fn immutable_data_len(&self) -> u32 {
466 self.immutable_data_len
467 }
468
469 pub fn set_immutable_data_len(&mut self, immutable_data_len: u32) {
471 self.immutable_data_len = immutable_data_len;
472 }
473}
474
475#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
477pub enum WriteOutcome {
478 New,
480 Overwritten(u32),
482 Taken(Vec<u8>),
488}
489
490impl WriteOutcome {
491 pub fn old_len(&self) -> u32 {
494 match self {
495 Self::New => 0,
496 Self::Overwritten(len) => *len,
497 Self::Taken(value) => value.len() as u32,
498 }
499 }
500
501 pub fn old_len_with_sentinel(&self) -> u32 {
509 match self {
510 Self::New => SENTINEL,
511 Self::Overwritten(len) => *len,
512 Self::Taken(value) => value.len() as u32,
513 }
514 }
515}
516
517#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, DefaultNoBound, Clone)]
523#[scale_info(skip_type_params(T))]
524pub struct DeletionQueueManager<T: Config> {
525 insert_counter: u32,
528 delete_counter: u32,
531
532 _phantom: PhantomData<T>,
533}
534
535struct DeletionQueueEntry<'a, T: Config> {
537 trie_id: TrieId,
539
540 queue: &'a mut DeletionQueueManager<T>,
543}
544
545impl<'a, T: Config> DeletionQueueEntry<'a, T> {
546 fn remove(self) {
548 <DeletionQueue<T>>::remove(self.queue.delete_counter);
549 self.queue.delete_counter = self.queue.delete_counter.wrapping_add(1);
550 <DeletionQueueCounter<T>>::set(self.queue.clone());
551 }
552}
553
554impl<T: Config> DeletionQueueManager<T> {
555 fn load() -> Self {
558 <DeletionQueueCounter<T>>::get()
559 }
560
561 fn is_empty(&self) -> bool {
563 self.insert_counter.wrapping_sub(self.delete_counter) == 0
564 }
565
566 fn insert(&mut self, trie_id: TrieId) {
568 <DeletionQueue<T>>::insert(self.insert_counter, trie_id);
569 self.insert_counter = self.insert_counter.wrapping_add(1);
570 <DeletionQueueCounter<T>>::set(self.clone());
571 }
572
573 fn next(&mut self) -> Option<DeletionQueueEntry<'_, T>> {
579 if self.is_empty() {
580 return None
581 }
582
583 let entry = <DeletionQueue<T>>::get(self.delete_counter);
584 entry.map(|trie_id| DeletionQueueEntry { trie_id, queue: self })
585 }
586}
587
588#[cfg(test)]
589impl<T: Config> DeletionQueueManager<T> {
590 pub fn from_test_values(insert_counter: u32, delete_counter: u32) -> Self {
591 Self { insert_counter, delete_counter, _phantom: Default::default() }
592 }
593 pub fn as_test_tuple(&self) -> (u32, u32) {
594 (self.insert_counter, self.delete_counter)
595 }
596}