1234567891011121314151617181920use crate::{21 exec::{AccountIdOf, StorageKey},22 BalanceOf, CodeHash, ContractInfoOf, Config, TrieId, AccountCounter, DeletionQueue, Error,23 weights::WeightInfo,24};25use codec::{Codec, Encode, Decode};26use sp_std::prelude::*;27use sp_std::{marker::PhantomData, fmt::Debug};28use sp_io::hashing::blake2_256;29use sp_runtime::{30 RuntimeDebug,31 traits::{Bounded, Saturating, Zero, Hash, Member, MaybeSerializeDeserialize},32};33use sp_core::crypto::UncheckedFrom;34use frame_support::{35 dispatch::{DispatchError, DispatchResult},36 storage::child::{self, KillChildStorageResult, ChildInfo},37 traits::Get,38 weights::Weight,39};4041pub type AliveContractInfo<T> =42 RawAliveContractInfo<CodeHash<T>, BalanceOf<T>, <T as frame_system::Config>::BlockNumber>;43pub type TombstoneContractInfo<T> = RawTombstoneContractInfo<44 <T as frame_system::Config>::Hash,45 <T as frame_system::Config>::Hashing,46>;47484950#[derive(Encode, Decode, RuntimeDebug)]51pub enum ContractInfo<T: Config> {52 Alive(AliveContractInfo<T>),53 Tombstone(TombstoneContractInfo<T>),54}5556impl<T: Config> ContractInfo<T> {57 58 pub fn get_alive(self) -> Option<AliveContractInfo<T>> {59 if let ContractInfo::Alive(alive) = self {60 Some(alive)61 } else {62 None63 }64 }65 66 pub fn as_alive(&self) -> Option<&AliveContractInfo<T>> {67 if let ContractInfo::Alive(ref alive) = self {68 Some(alive)69 } else {70 None71 }72 }7374 75 pub fn get_tombstone(self) -> Option<TombstoneContractInfo<T>> {76 if let ContractInfo::Tombstone(tombstone) = self {77 Some(tombstone)78 } else {79 None80 }81 }82}83848586#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]87pub struct RawAliveContractInfo<CodeHash, Balance, BlockNumber> {88 89 pub trie_id: TrieId,90 91 92 93 pub storage_size: u32,94 95 pub pair_count: u32,96 97 pub code_hash: CodeHash,98 99 pub rent_allowance: Balance,100 101 102 103 pub rent_payed: Balance,104 105 pub deduct_block: BlockNumber,106 107 pub last_write: Option<BlockNumber>,108 109 pub _reserved: Option<()>,110}111112impl<CodeHash, Balance, BlockNumber> RawAliveContractInfo<CodeHash, Balance, BlockNumber> {113 114 pub fn child_trie_info(&self) -> ChildInfo {115 child_trie_info(&self.trie_id[..])116 }117}118119120fn child_trie_info(trie_id: &[u8]) -> ChildInfo {121 ChildInfo::new_default(trie_id)122}123124#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug)]125pub struct RawTombstoneContractInfo<H, Hasher>(H, PhantomData<Hasher>);126127impl<H, Hasher> RawTombstoneContractInfo<H, Hasher>128where129 H: Member130 + MaybeSerializeDeserialize131 + Debug132 + AsRef<[u8]>133 + AsMut<[u8]>134 + Copy135 + Default136 + sp_std::hash::Hash137 + Codec,138 Hasher: Hash<Output = H>,139{140 pub fn new(storage_root: &[u8], code_hash: H) -> Self {141 let mut buf = Vec::new();142 storage_root.using_encoded(|encoded| buf.extend_from_slice(encoded));143 buf.extend_from_slice(code_hash.as_ref());144 RawTombstoneContractInfo(<Hasher as Hash>::hash(&buf[..]), PhantomData)145 }146}147148impl<T: Config> From<AliveContractInfo<T>> for ContractInfo<T> {149 fn from(alive_info: AliveContractInfo<T>) -> Self {150 Self::Alive(alive_info)151 }152}153154155156#[cfg_attr(test, derive(PartialEq, Eq, Debug))]157pub struct ContractAbsentError;158159#[derive(Encode, Decode)]160pub struct DeletedContract {161 pair_count: u32,162 trie_id: TrieId,163}164165pub struct Storage<T>(PhantomData<T>);166167impl<T> Storage<T>168where169 T: Config,170 T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,171{172 173 174 175 176 pub fn read(trie_id: &TrieId, key: &StorageKey) -> Option<Vec<u8>> {177 child::get_raw(&child_trie_info(&trie_id), &blake2_256(key))178 }179180 181 182 183 184 185 186 187 188 189 190 191 192 193 pub fn write(194 account: &AccountIdOf<T>,195 trie_id: &TrieId,196 key: &StorageKey,197 opt_new_value: Option<Vec<u8>>,198 ) -> DispatchResult {199 let mut new_info = match <ContractInfoOf<T>>::get(account) {200 Some(ContractInfo::Alive(alive)) => alive,201 None | Some(ContractInfo::Tombstone(_)) => panic!("Contract not found"),202 };203204 let hashed_key = blake2_256(key);205 let child_trie_info = &child_trie_info(&trie_id);206207 let opt_prev_len = child::len(&child_trie_info, &hashed_key);208209 210 match (&opt_prev_len, &opt_new_value) {211 (Some(_), None) => {212 new_info.pair_count = new_info213 .pair_count214 .checked_sub(1)215 .ok_or_else(|| Error::<T>::StorageExhausted)?;216 }217 (None, Some(_)) => {218 new_info.pair_count = new_info219 .pair_count220 .checked_add(1)221 .ok_or_else(|| Error::<T>::StorageExhausted)?;222 }223 (Some(_), Some(_)) => {}224 (None, None) => {}225 }226227 228 let prev_value_len = opt_prev_len.unwrap_or(0);229 let new_value_len = opt_new_value230 .as_ref()231 .map(|new_value| new_value.len() as u32)232 .unwrap_or(0);233 new_info.storage_size = new_info234 .storage_size235 .checked_sub(prev_value_len)236 .and_then(|val| val.checked_add(new_value_len))237 .ok_or_else(|| Error::<T>::StorageExhausted)?;238239 new_info.last_write = Some(<frame_system::Pallet<T>>::block_number());240 <ContractInfoOf<T>>::insert(&account, ContractInfo::Alive(new_info));241242 243 match opt_new_value {244 Some(new_value) => child::put_raw(&child_trie_info, &hashed_key, &new_value[..]),245 None => child::kill(&child_trie_info, &hashed_key),246 }247248 Ok(())249 }250251 252 pub fn rent_allowance(account: &AccountIdOf<T>) -> Result<BalanceOf<T>, ContractAbsentError> {253 <ContractInfoOf<T>>::get(account)254 .and_then(|i| i.as_alive().map(|i| i.rent_allowance))255 .ok_or(ContractAbsentError)256 }257258 259 260 261 pub fn set_rent_allowance(262 account: &AccountIdOf<T>,263 rent_allowance: BalanceOf<T>,264 ) -> Result<(), ContractAbsentError> {265 <ContractInfoOf<T>>::mutate(account, |maybe_contract_info| match maybe_contract_info {266 Some(ContractInfo::Alive(ref mut alive_info)) => {267 alive_info.rent_allowance = rent_allowance;268 Ok(())269 }270 _ => Err(ContractAbsentError),271 })272 }273274 275 276 277 pub fn place_contract(278 account: &AccountIdOf<T>,279 trie_id: TrieId,280 ch: CodeHash<T>,281 ) -> Result<AliveContractInfo<T>, DispatchError> {282 <ContractInfoOf<T>>::try_mutate(account, |existing| {283 if existing.is_some() {284 return Err(Error::<T>::DuplicateContract.into());285 }286287 let contract = AliveContractInfo::<T> {288 code_hash: ch,289 storage_size: 0,290 trie_id,291 deduct_block:292 293 294 295 <frame_system::Pallet<T>>::block_number().saturating_sub(1u32.into()),296 rent_allowance: <BalanceOf<T>>::max_value(),297 rent_payed: <BalanceOf<T>>::zero(),298 pair_count: 0,299 last_write: None,300 _reserved: None,301 };302303 *existing = Some(contract.clone().into());304305 Ok(contract)306 })307 }308309 310 311 312 313 pub fn queue_trie_for_deletion(contract: &AliveContractInfo<T>) -> DispatchResult {314 if <DeletionQueue<T>>::decode_len().unwrap_or(0) >= T::DeletionQueueDepth::get() as usize {315 Err(Error::<T>::DeletionQueueFull.into())316 } else {317 <DeletionQueue<T>>::append(DeletedContract {318 pair_count: contract.pair_count,319 trie_id: contract.trie_id.clone(),320 });321 Ok(())322 }323 }324325 326 327 328 pub fn deletion_budget(queue_len: usize, weight_limit: Weight) -> (u64, u32) {329 let base_weight = T::WeightInfo::on_initialize();330 let weight_per_queue_item = T::WeightInfo::on_initialize_per_queue_item(1)331 - T::WeightInfo::on_initialize_per_queue_item(0);332 let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1)333 - T::WeightInfo::on_initialize_per_trie_key(0);334 let decoding_weight = weight_per_queue_item.saturating_mul(queue_len as Weight);335336 337 338 let key_budget = weight_limit339 .saturating_sub(base_weight)340 .saturating_sub(decoding_weight)341 .checked_div(weight_per_key)342 .unwrap_or(0) as u32;343344 (weight_per_key, key_budget)345 }346347 348 349 350 351 pub fn process_deletion_queue_batch(weight_limit: Weight) -> Weight {352 let queue_len = <DeletionQueue<T>>::decode_len().unwrap_or(0);353 if queue_len == 0 {354 return weight_limit;355 }356357 let (weight_per_key, mut remaining_key_budget) =358 Self::deletion_budget(queue_len, weight_limit);359360 361 362 363 if remaining_key_budget == 0 {364 return weight_limit;365 }366367 let mut queue = <DeletionQueue<T>>::get();368369 while !queue.is_empty() && remaining_key_budget > 0 {370 371 let trie = &mut queue[0];372 let pair_count = trie.pair_count;373 let outcome =374 child::kill_storage(&child_trie_info(&trie.trie_id), Some(remaining_key_budget));375 if pair_count > remaining_key_budget {376 377 trie.pair_count -= remaining_key_budget;378 } else {379 380 381 let removed = queue.swap_remove(0);382 match outcome {383 384 KillChildStorageResult::SomeRemaining(_) => {385 log::error!(386 target: "runtime::contracts",387 "After deletion keys are remaining in this child trie: {:?}",388 removed.trie_id,389 );390 }391 KillChildStorageResult::AllRemoved(_) => (),392 }393 }394 remaining_key_budget =395 remaining_key_budget.saturating_sub(remaining_key_budget.min(pair_count));396 }397398 <DeletionQueue<T>>::put(queue);399 weight_limit.saturating_sub(weight_per_key.saturating_mul(remaining_key_budget as Weight))400 }401402 403 404 pub fn generate_trie_id(account_id: &AccountIdOf<T>) -> TrieId {405 406 407 let new_seed = <AccountCounter<T>>::mutate(|v| {408 *v = v.wrapping_add(1);409 *v410 });411412 let buf: Vec<_> = account_id413 .as_ref()414 .iter()415 .chain(&new_seed.to_le_bytes())416 .cloned()417 .collect();418 T::Hashing::hash(&buf).as_ref().into()419 }420421 422 #[cfg(test)]423 pub fn code_hash(account: &AccountIdOf<T>) -> Result<CodeHash<T>, ContractAbsentError> {424 <ContractInfoOf<T>>::get(account)425 .and_then(|i| i.as_alive().map(|i| i.code_hash))426 .ok_or(ContractAbsentError)427 }428429 430 #[cfg(test)]431 pub fn fill_queue_with_dummies() {432 let queue: Vec<_> = (0..T::DeletionQueueDepth::get())433 .map(|_| DeletedContract {434 pair_count: 0,435 trie_id: vec![],436 })437 .collect();438 <DeletionQueue<T>>::put(queue);439 }440}