mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 15:11:03 +00:00
Make automatic storage deposits resistant against changing deposit prices (#12083)
* Require `FixedPointOperand` for Balances * Delay deposit calculation * Make refunds pro rata of consumed storage * Add storage migration * Fix clippy * Add liquidity checks * Fixe delayed deposit limit enforcement * Defer charges * Import Vec * Add try-runtime hooks for migration * Fix warning * Adapt to new OnRuntimeUpgrade trait * Apply suggestions from code review Co-authored-by: Sasha Gryaznov <hi@agryaznov.com> * fmt * Apply suggestions from code review Co-authored-by: Sasha Gryaznov <hi@agryaznov.com> * More suggestions from code review Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
This commit is contained in:
committed by
GitHub
parent
cb82064cb8
commit
857c3bf37b
@@ -18,37 +18,80 @@
|
||||
use crate::{BalanceOf, CodeHash, Config, Pallet, TrieId, Weight};
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::{
|
||||
codec, pallet_prelude::*, storage::migration, storage_alias, traits::Get, Identity,
|
||||
Twox64Concat,
|
||||
codec,
|
||||
pallet_prelude::*,
|
||||
storage::migration,
|
||||
storage_alias,
|
||||
traits::{Get, OnRuntimeUpgrade},
|
||||
Identity, Twox64Concat,
|
||||
};
|
||||
use sp_runtime::traits::Saturating;
|
||||
use sp_std::{marker::PhantomData, prelude::*};
|
||||
|
||||
/// Wrapper for all migrations of this pallet, based on `StorageVersion`.
|
||||
pub fn migrate<T: Config>() -> Weight {
|
||||
let version = StorageVersion::get::<Pallet<T>>();
|
||||
let mut weight = Weight::zero();
|
||||
/// Performs all necessary migrations based on `StorageVersion`.
|
||||
pub struct Migration<T: Config>(PhantomData<T>);
|
||||
impl<T: Config> OnRuntimeUpgrade for Migration<T> {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
let version = StorageVersion::get::<Pallet<T>>();
|
||||
let mut weight = Weight::zero();
|
||||
|
||||
if version < 4 {
|
||||
weight = weight.saturating_add(v4::migrate::<T>());
|
||||
StorageVersion::new(4).put::<Pallet<T>>();
|
||||
if version < 4 {
|
||||
weight = weight.saturating_add(v4::migrate::<T>());
|
||||
StorageVersion::new(4).put::<Pallet<T>>();
|
||||
}
|
||||
|
||||
if version < 5 {
|
||||
weight = weight.saturating_add(v5::migrate::<T>());
|
||||
StorageVersion::new(5).put::<Pallet<T>>();
|
||||
}
|
||||
|
||||
if version < 6 {
|
||||
weight = weight.saturating_add(v6::migrate::<T>());
|
||||
StorageVersion::new(6).put::<Pallet<T>>();
|
||||
}
|
||||
|
||||
if version < 7 {
|
||||
weight = weight.saturating_add(v7::migrate::<T>());
|
||||
StorageVersion::new(7).put::<Pallet<T>>();
|
||||
}
|
||||
|
||||
if version < 8 {
|
||||
weight = weight.saturating_add(v8::migrate::<T>());
|
||||
StorageVersion::new(8).put::<Pallet<T>>();
|
||||
}
|
||||
|
||||
weight
|
||||
}
|
||||
|
||||
if version < 5 {
|
||||
weight = weight.saturating_add(v5::migrate::<T>());
|
||||
StorageVersion::new(5).put::<Pallet<T>>();
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn pre_upgrade() -> Result<Vec<u8>, &'static str> {
|
||||
let version = StorageVersion::get::<Pallet<T>>();
|
||||
|
||||
if version < 7 {
|
||||
return Ok(vec![])
|
||||
}
|
||||
|
||||
if version < 8 {
|
||||
v8::pre_upgrade::<T>()?;
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
if version < 6 {
|
||||
weight = weight.saturating_add(v6::migrate::<T>());
|
||||
StorageVersion::new(6).put::<Pallet<T>>();
|
||||
}
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> {
|
||||
let version = StorageVersion::get::<Pallet<T>>();
|
||||
|
||||
if version < 7 {
|
||||
weight = weight.saturating_add(v7::migrate::<T>());
|
||||
StorageVersion::new(7).put::<Pallet<T>>();
|
||||
}
|
||||
if version < 7 {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
weight
|
||||
if version < 8 {
|
||||
v8::post_upgrade::<T>()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// V4: `Schedule` is changed to be a config item rather than an in-storage value.
|
||||
@@ -185,9 +228,9 @@ mod v6 {
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct RawContractInfo<CodeHash, Balance> {
|
||||
trie_id: TrieId,
|
||||
code_hash: CodeHash,
|
||||
storage_deposit: Balance,
|
||||
pub trie_id: TrieId,
|
||||
pub code_hash: CodeHash,
|
||||
pub storage_deposit: Balance,
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
@@ -199,7 +242,7 @@ mod v6 {
|
||||
refcount: u64,
|
||||
}
|
||||
|
||||
type ContractInfo<T> = RawContractInfo<CodeHash<T>, BalanceOf<T>>;
|
||||
pub type ContractInfo<T> = RawContractInfo<CodeHash<T>, BalanceOf<T>>;
|
||||
|
||||
#[storage_alias]
|
||||
type ContractInfoOf<T: Config> = StorageMap<
|
||||
@@ -266,3 +309,108 @@ mod v7 {
|
||||
T::DbWeight::get().reads_writes(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
/// Update `ContractInfo` with new fields that track storage deposits.
|
||||
mod v8 {
|
||||
use super::*;
|
||||
use sp_io::default_child_storage as child;
|
||||
use v6::ContractInfo as OldContractInfo;
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
struct ContractInfo<T: Config> {
|
||||
trie_id: TrieId,
|
||||
code_hash: CodeHash<T>,
|
||||
storage_bytes: u32,
|
||||
storage_items: u32,
|
||||
storage_byte_deposit: BalanceOf<T>,
|
||||
storage_item_deposit: BalanceOf<T>,
|
||||
storage_base_deposit: BalanceOf<T>,
|
||||
}
|
||||
|
||||
#[storage_alias]
|
||||
type ContractInfoOf<T: Config, V> =
|
||||
StorageMap<Pallet<T>, Twox64Concat, <T as frame_system::Config>::AccountId, V>;
|
||||
|
||||
pub fn migrate<T: Config>() -> Weight {
|
||||
let mut weight = Weight::zero();
|
||||
|
||||
<ContractInfoOf<T, ContractInfo<T>>>::translate_values(|old: OldContractInfo<T>| {
|
||||
// Count storage items of this contract
|
||||
let mut storage_bytes = 0u32;
|
||||
let mut storage_items = 0u32;
|
||||
let mut key = Vec::new();
|
||||
while let Some(next) = child::next_key(&old.trie_id, &key) {
|
||||
key = next;
|
||||
let mut val_out = [];
|
||||
let len = child::read(&old.trie_id, &key, &mut val_out, 0)
|
||||
.expect("The loop conditions checks for existence of the key; qed");
|
||||
storage_bytes.saturating_accrue(len);
|
||||
storage_items.saturating_accrue(1);
|
||||
}
|
||||
|
||||
let storage_byte_deposit =
|
||||
T::DepositPerByte::get().saturating_mul(storage_bytes.into());
|
||||
let storage_item_deposit =
|
||||
T::DepositPerItem::get().saturating_mul(storage_items.into());
|
||||
let storage_base_deposit = old
|
||||
.storage_deposit
|
||||
.saturating_sub(storage_byte_deposit)
|
||||
.saturating_sub(storage_item_deposit);
|
||||
|
||||
// Reads: One read for each storage item plus the contract info itself.
|
||||
// Writes: Only the new contract info.
|
||||
weight = weight
|
||||
.saturating_add(T::DbWeight::get().reads_writes(u64::from(storage_items) + 1, 1));
|
||||
|
||||
Some(ContractInfo {
|
||||
trie_id: old.trie_id,
|
||||
code_hash: old.code_hash,
|
||||
storage_bytes,
|
||||
storage_items,
|
||||
storage_byte_deposit,
|
||||
storage_item_deposit,
|
||||
storage_base_deposit,
|
||||
})
|
||||
});
|
||||
|
||||
weight
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
pub fn pre_upgrade<T: Config>() -> Result<(), &'static str> {
|
||||
use frame_support::traits::ReservableCurrency;
|
||||
for (key, value) in ContractInfoOf::<T, OldContractInfo<T>>::iter() {
|
||||
let reserved = T::Currency::reserved_balance(&key);
|
||||
ensure!(reserved >= value.storage_deposit, "Reserved balance out of sync.");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
pub fn post_upgrade<T: Config>() -> Result<(), &'static str> {
|
||||
use frame_support::traits::ReservableCurrency;
|
||||
for (key, value) in ContractInfoOf::<T, ContractInfo<T>>::iter() {
|
||||
let reserved = T::Currency::reserved_balance(&key);
|
||||
let stored = value
|
||||
.storage_base_deposit
|
||||
.saturating_add(value.storage_byte_deposit)
|
||||
.saturating_add(value.storage_item_deposit);
|
||||
ensure!(reserved >= stored, "Reserved balance out of sync.");
|
||||
|
||||
let mut storage_bytes = 0u32;
|
||||
let mut storage_items = 0u32;
|
||||
let mut key = Vec::new();
|
||||
while let Some(next) = child::next_key(&value.trie_id, &key) {
|
||||
key = next;
|
||||
let mut val_out = [];
|
||||
let len = child::read(&value.trie_id, &key, &mut val_out, 0)
|
||||
.expect("The loop conditions checks for existence of the key; qed");
|
||||
storage_bytes.saturating_accrue(len);
|
||||
storage_items.saturating_accrue(1);
|
||||
}
|
||||
ensure!(storage_bytes == value.storage_bytes, "Storage bytes do not match.",);
|
||||
ensure!(storage_items == value.storage_items, "Storage items do not match.",);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user