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 Debug, DispatchError,
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 AccountIdOrAddress::Address(address) => *address,
124 }
125 }
126
127 pub fn account_id(&self) -> AccountIdOf<T> {
128 match self {
129 AccountIdOrAddress::AccountId(id) => id.clone(),
130 AccountIdOrAddress::Address(address) => T::AddressMapper::to_account_id(address),
131 }
132 }
133}
134
135impl<T: Config> From<ContractInfo<T>> for AccountType<T> {
136 fn from(contract_info: ContractInfo<T>) -> Self {
137 AccountType::Contract(contract_info)
138 }
139}
140
141impl<T: Config> AccountInfo<T> {
142 pub fn is_contract(address: &H160) -> bool {
144 let Some(info) = <AccountInfoOf<T>>::get(address) else { return false };
145 matches!(info.account_type, AccountType::Contract(_))
146 }
147
148 pub fn balance_of(account: AccountIdOrAddress<T>) -> BalanceWithDust<BalanceOf<T>> {
150 let info = <AccountInfoOf<T>>::get(account.address()).unwrap_or_default();
151 info.balance(&account.account_id(), Preservation::Preserve)
152 }
153
154 pub fn balance(
156 &self,
157 account: &AccountIdOf<T>,
158 preservation: Preservation,
159 ) -> BalanceWithDust<BalanceOf<T>> {
160 let value = T::Currency::reducible_balance(account, preservation, Fortitude::Polite);
161 BalanceWithDust::new_unchecked::<T>(value, self.dust)
162 }
163
164 pub fn total_balance(account: AccountIdOrAddress<T>) -> BalanceWithDust<BalanceOf<T>> {
166 let value = T::Currency::total_balance(&account.account_id());
167 let dust = <AccountInfoOf<T>>::get(account.address()).map(|a| a.dust).unwrap_or_default();
168 BalanceWithDust::new_unchecked::<T>(value, dust)
169 }
170
171 pub fn load_contract(address: &H160) -> Option<ContractInfo<T>> {
173 let Some(info) = <AccountInfoOf<T>>::get(address) else { return None };
174 let AccountType::Contract(contract_info) = info.account_type else { return None };
175 Some(contract_info)
176 }
177
178 pub fn insert_contract(address: &H160, contract: ContractInfo<T>) {
180 AccountInfoOf::<T>::mutate(address, |account| {
181 if let Some(account) = account {
182 account.account_type = contract.clone().into();
183 } else {
184 *account = Some(AccountInfo { account_type: contract.clone().into(), dust: 0 });
185 }
186 });
187 }
188}
189
190impl<T: Config> ContractInfo<T> {
191 pub fn new(
196 address: &H160,
197 nonce: T::Nonce,
198 code_hash: sp_core::H256,
199 ) -> Result<Self, DispatchError> {
200 if <AccountInfo<T>>::is_contract(address) {
201 return Err(Error::<T>::DuplicateContract.into());
202 }
203
204 let trie_id = {
205 let buf = ("bcontract_trie_v1", address, nonce).using_encoded(T::Hashing::hash);
206 buf.as_ref()
207 .to_vec()
208 .try_into()
209 .expect("Runtime uses a reasonable hash size. Hence sizeof(T::Hash) <= 128; qed")
210 };
211
212 let contract = Self {
213 trie_id,
214 code_hash,
215 storage_bytes: 0,
216 storage_items: 0,
217 storage_byte_deposit: Zero::zero(),
218 storage_item_deposit: Zero::zero(),
219 storage_base_deposit: Zero::zero(),
220 immutable_data_len: 0,
221 };
222
223 Ok(contract)
224 }
225
226 pub fn child_trie_info(&self) -> ChildInfo {
228 ChildInfo::new_default(self.trie_id.as_ref())
229 }
230
231 pub fn extra_deposit(&self) -> BalanceOf<T> {
233 self.storage_byte_deposit.saturating_add(self.storage_item_deposit)
234 }
235
236 pub fn total_deposit(&self) -> BalanceOf<T> {
238 self.extra_deposit().saturating_add(self.storage_base_deposit)
239 }
240
241 pub fn storage_base_deposit(&self) -> BalanceOf<T> {
243 self.storage_base_deposit
244 }
245
246 pub fn read(&self, key: &Key) -> Option<Vec<u8>> {
251 let value = child::get_raw(&self.child_trie_info(), key.hash().as_slice());
252 log::trace!(target: crate::LOG_TARGET, "contract storage: read value {:?} for key {:x?}", value, key);
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 frame_meter: Option<&mut FrameMeter<T>>,
279 take: bool,
280 ) -> Result<WriteOutcome, DispatchError> {
281 log::trace!(target: crate::LOG_TARGET, "contract storage: writing value {:?} for key {:x?}", new_value, key);
282 let hashed_key = key.hash();
283 if_tracing(|t| {
284 let old = child::get_raw(&self.child_trie_info(), hashed_key.as_slice());
285 t.storage_write(key, old, new_value.as_deref());
286 });
287
288 self.write_raw(&hashed_key, new_value.as_deref(), frame_meter, take)
289 }
290
291 #[cfg(feature = "runtime-benchmarks")]
294 pub fn bench_write_raw(
295 &self,
296 key: &[u8],
297 new_value: Option<Vec<u8>>,
298 take: bool,
299 ) -> Result<WriteOutcome, DispatchError> {
300 self.write_raw(key, new_value.as_deref(), None, take)
301 }
302
303 fn write_raw(
304 &self,
305 key: &[u8],
306 new_value: Option<&[u8]>,
307 frame_meter: Option<&mut FrameMeter<T>>,
308 take: bool,
309 ) -> Result<WriteOutcome, DispatchError> {
310 let child_trie_info = &self.child_trie_info();
311 let (old_len, old_value) = if take {
312 let val = child::get_raw(child_trie_info, key);
313 (val.as_ref().map(|v| v.len() as u32), val)
314 } else {
315 (child::len(child_trie_info, key), None)
316 };
317
318 if let Some(frame_meter) = frame_meter {
319 let mut diff = Diff::default();
320 let key_len = key.len() as u32;
321 match (old_len, new_value.as_ref().map(|v| v.len() as u32)) {
322 (Some(old_len), Some(new_len)) =>
323 if new_len > old_len {
324 diff.bytes_added = new_len - old_len;
325 } else {
326 diff.bytes_removed = old_len - new_len;
327 },
328 (None, Some(new_len)) => {
329 diff.bytes_added = new_len.saturating_add(key_len);
330 diff.items_added = 1;
331 },
332 (Some(old_len), None) => {
333 diff.bytes_removed = old_len.saturating_add(key_len);
334 diff.items_removed = 1;
335 },
336 (None, None) => (),
337 }
338 frame_meter.record_contract_storage_changes(&diff)?;
339 }
340
341 match &new_value {
342 Some(new_value) => child::put_raw(child_trie_info, key, new_value),
343 None => child::kill(child_trie_info, key),
344 }
345
346 Ok(match (old_len, old_value) {
347 (None, _) => WriteOutcome::New,
348 (Some(old_len), None) => WriteOutcome::Overwritten(old_len),
349 (Some(_), Some(old_value)) => WriteOutcome::Taken(old_value),
350 })
351 }
352
353 pub fn update_base_deposit(&mut self, code_deposit: BalanceOf<T>) -> BalanceOf<T> {
359 let contract_deposit = {
360 let bytes_added: u32 =
361 (self.encoded_size() as u32).saturating_add(self.immutable_data_len);
362 let items_added: u32 = if self.immutable_data_len == 0 { 1 } else { 2 };
363
364 T::DepositPerByte::get()
365 .saturating_mul(bytes_added.into())
366 .saturating_add(T::DepositPerItem::get().saturating_mul(items_added.into()))
367 };
368
369 let code_deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_deposit);
373
374 let deposit = contract_deposit.saturating_add(code_deposit);
375 self.storage_base_deposit = deposit;
376 deposit
377 }
378
379 pub fn queue_trie_for_deletion(trie_id: TrieId) {
383 DeletionQueueManager::<T>::load().insert(trie_id);
384 }
385
386 pub fn deletion_budget(meter: &WeightMeter) -> (Weight, u32) {
389 let base_weight = T::WeightInfo::on_process_deletion_queue_batch();
390 let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) -
391 T::WeightInfo::on_initialize_per_trie_key(0);
392
393 let key_budget = meter
396 .limit()
397 .saturating_sub(base_weight)
398 .checked_div_per_component(&weight_per_key)
399 .unwrap_or(0) as u32;
400
401 (weight_per_key, key_budget)
402 }
403
404 pub fn process_deletion_queue_batch(meter: &mut WeightMeter) {
406 if meter.try_consume(T::WeightInfo::on_process_deletion_queue_batch()).is_err() {
407 return
408 };
409
410 let mut queue = <DeletionQueueManager<T>>::load();
411 if queue.is_empty() {
412 return;
413 }
414
415 let (weight_per_key, budget) = Self::deletion_budget(&meter);
416 let mut remaining_key_budget = budget;
417 while remaining_key_budget > 0 {
418 let Some(entry) = queue.next() else { break };
419
420 #[allow(deprecated)]
421 let outcome = child::kill_storage(
422 &ChildInfo::new_default(&entry.trie_id),
423 Some(remaining_key_budget),
424 );
425
426 match outcome {
427 KillStorageResult::SomeRemaining(keys_removed) => {
429 remaining_key_budget.saturating_reduce(keys_removed);
430 break
431 },
432 KillStorageResult::AllRemoved(keys_removed) => {
433 entry.remove();
434 remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed.max(1));
436 },
437 };
438 }
439
440 meter.consume(weight_per_key.saturating_mul(u64::from(budget - remaining_key_budget)))
441 }
442
443 pub fn load_code_hash(account: &AccountIdOf<T>) -> Option<sp_core::H256> {
445 <AccountInfo<T>>::load_contract(&T::AddressMapper::to_address(account)).map(|i| i.code_hash)
446 }
447
448 pub fn immutable_data_len(&self) -> u32 {
450 self.immutable_data_len
451 }
452
453 pub fn set_immutable_data_len(&mut self, immutable_data_len: u32) {
455 self.immutable_data_len = immutable_data_len;
456 }
457}
458
459#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
461pub enum WriteOutcome {
462 New,
464 Overwritten(u32),
466 Taken(Vec<u8>),
472}
473
474impl WriteOutcome {
475 pub fn old_len(&self) -> u32 {
478 match self {
479 Self::New => 0,
480 Self::Overwritten(len) => *len,
481 Self::Taken(value) => value.len() as u32,
482 }
483 }
484
485 pub fn old_len_with_sentinel(&self) -> u32 {
493 match self {
494 Self::New => SENTINEL,
495 Self::Overwritten(len) => *len,
496 Self::Taken(value) => value.len() as u32,
497 }
498 }
499}
500
501#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, DefaultNoBound, Clone)]
507#[scale_info(skip_type_params(T))]
508pub struct DeletionQueueManager<T: Config> {
509 insert_counter: u32,
512 delete_counter: u32,
515
516 _phantom: PhantomData<T>,
517}
518
519struct DeletionQueueEntry<'a, T: Config> {
521 trie_id: TrieId,
523
524 queue: &'a mut DeletionQueueManager<T>,
527}
528
529impl<'a, T: Config> DeletionQueueEntry<'a, T> {
530 fn remove(self) {
532 <DeletionQueue<T>>::remove(self.queue.delete_counter);
533 self.queue.delete_counter = self.queue.delete_counter.wrapping_add(1);
534 <DeletionQueueCounter<T>>::set(self.queue.clone());
535 }
536}
537
538impl<T: Config> DeletionQueueManager<T> {
539 fn load() -> Self {
542 <DeletionQueueCounter<T>>::get()
543 }
544
545 fn is_empty(&self) -> bool {
547 self.insert_counter.wrapping_sub(self.delete_counter) == 0
548 }
549
550 fn insert(&mut self, trie_id: TrieId) {
552 <DeletionQueue<T>>::insert(self.insert_counter, trie_id);
553 self.insert_counter = self.insert_counter.wrapping_add(1);
554 <DeletionQueueCounter<T>>::set(self.clone());
555 }
556
557 fn next(&mut self) -> Option<DeletionQueueEntry<'_, T>> {
563 if self.is_empty() {
564 return None
565 }
566
567 let entry = <DeletionQueue<T>>::get(self.delete_counter);
568 entry.map(|trie_id| DeletionQueueEntry { trie_id, queue: self })
569 }
570}
571
572#[cfg(test)]
573impl<T: Config> DeletionQueueManager<T> {
574 pub fn from_test_values(insert_counter: u32, delete_counter: u32) -> Self {
575 Self { insert_counter, delete_counter, _phantom: Default::default() }
576 }
577 pub fn as_test_tuple(&self) -> (u32, u32) {
578 (self.insert_counter, self.delete_counter)
579 }
580}