mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 19:51:05 +00:00
generate_storage_alias: Rewrite as proc macro attribute (#11387)
* generate_storage_alias: Rewrite as proc macro attribute
This rewrites the `generate_storage_alias!` declarative macro as proc-macro attribute. While doing
this the name is changed to `storage_alias`. The prefix can now also be the name of a pallet. This
makes storage aliases work in migrations for all kind of chains and not just for the ones that use
predefined prefixes.
* Fix compilation and FMT
* Moare fixes
* 🤦
* ......
* Rework the syntax and support instancing
* FMT
* Prefix variants with `Storage`
* Make it compile
* Fix where clause on rust stable
This commit is contained in:
@@ -368,12 +368,10 @@ mod list {
|
|||||||
assert_eq!(crate::ListNodes::<Runtime>::count(), 4);
|
assert_eq!(crate::ListNodes::<Runtime>::count(), 4);
|
||||||
// we do some wacky stuff here to get access to the counter, since it is (reasonably)
|
// we do some wacky stuff here to get access to the counter, since it is (reasonably)
|
||||||
// not exposed as mutable in any sense.
|
// not exposed as mutable in any sense.
|
||||||
frame_support::generate_storage_alias!(
|
#[frame_support::storage_alias]
|
||||||
BagsList,
|
type CounterForListNodes<T: Config> =
|
||||||
CounterForListNodes
|
StorageValue<crate::Pallet<T>, u32, frame_support::pallet_prelude::ValueQuery>;
|
||||||
=> Value<u32, frame_support::pallet_prelude::ValueQuery>
|
CounterForListNodes::<Runtime>::mutate(|counter| *counter += 1);
|
||||||
);
|
|
||||||
CounterForListNodes::mutate(|counter| *counter += 1);
|
|
||||||
assert_eq!(crate::ListNodes::<Runtime>::count(), 5);
|
assert_eq!(crate::ListNodes::<Runtime>::count(), 5);
|
||||||
|
|
||||||
assert_eq!(List::<Runtime>::sanity_check(), Err("iter_count != stored_count"));
|
assert_eq!(List::<Runtime>::sanity_check(), Err("iter_count != stored_count"));
|
||||||
|
|||||||
@@ -30,11 +30,12 @@ impl<T: crate::Config> OnRuntimeUpgrade for CheckCounterPrefix<T> {
|
|||||||
fn pre_upgrade() -> Result<(), &'static str> {
|
fn pre_upgrade() -> Result<(), &'static str> {
|
||||||
use frame_support::ensure;
|
use frame_support::ensure;
|
||||||
// The old explicit storage item.
|
// The old explicit storage item.
|
||||||
frame_support::generate_storage_alias!(BagsList, CounterForListNodes => Value<u32>);
|
#[frame_support::storage_alias]
|
||||||
|
type CounterForListNodes<T: crate::Config> = StorageValue<crate::Pallet<T>, u32>;
|
||||||
|
|
||||||
// ensure that a value exists in the counter struct.
|
// ensure that a value exists in the counter struct.
|
||||||
ensure!(
|
ensure!(
|
||||||
crate::ListNodes::<T>::count() == CounterForListNodes::get().unwrap(),
|
crate::ListNodes::<T>::count() == CounterForListNodes::<T>::get().unwrap(),
|
||||||
"wrong list node counter"
|
"wrong list node counter"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,8 @@
|
|||||||
use crate::{BalanceOf, CodeHash, Config, Pallet, TrieId, Weight};
|
use crate::{BalanceOf, CodeHash, Config, Pallet, TrieId, Weight};
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
codec, generate_storage_alias,
|
codec, pallet_prelude::*, storage::migration, storage_alias, traits::Get, Identity,
|
||||||
pallet_prelude::*,
|
Twox64Concat,
|
||||||
storage::migration,
|
|
||||||
traits::{Get, PalletInfoAccess},
|
|
||||||
Identity, Twox64Concat,
|
|
||||||
};
|
};
|
||||||
use sp_std::{marker::PhantomData, prelude::*};
|
use sp_std::{marker::PhantomData, prelude::*};
|
||||||
|
|
||||||
@@ -117,15 +114,16 @@ mod v5 {
|
|||||||
trie_id: TrieId,
|
trie_id: TrieId,
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_storage_alias!(
|
#[storage_alias]
|
||||||
Contracts,
|
type ContractInfoOf<T: Config> = StorageMap<
|
||||||
ContractInfoOf<T: Config> => Map<(Twox64Concat, T::AccountId), ContractInfo<T>>
|
Pallet<T>,
|
||||||
);
|
Twox64Concat,
|
||||||
|
<T as frame_system::Config>::AccountId,
|
||||||
|
ContractInfo<T>,
|
||||||
|
>;
|
||||||
|
|
||||||
generate_storage_alias!(
|
#[storage_alias]
|
||||||
Contracts,
|
type DeletionQueue<T: Config> = StorageValue<Pallet<T>, Vec<DeletedContract>>;
|
||||||
DeletionQueue => Value<Vec<DeletedContract>>
|
|
||||||
);
|
|
||||||
|
|
||||||
pub fn migrate<T: Config>() -> Weight {
|
pub fn migrate<T: Config>() -> Weight {
|
||||||
let mut weight: Weight = 0;
|
let mut weight: Weight = 0;
|
||||||
@@ -142,7 +140,7 @@ mod v5 {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
DeletionQueue::translate(|old: Option<Vec<OldDeletedContract>>| {
|
DeletionQueue::<T>::translate(|old: Option<Vec<OldDeletedContract>>| {
|
||||||
weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
|
weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
|
||||||
old.map(|old| old.into_iter().map(|o| DeletedContract { trie_id: o.trie_id }).collect())
|
old.map(|old| old.into_iter().map(|o| DeletedContract { trie_id: o.trie_id }).collect())
|
||||||
})
|
})
|
||||||
@@ -202,20 +200,19 @@ mod v6 {
|
|||||||
|
|
||||||
type ContractInfo<T> = RawContractInfo<CodeHash<T>, BalanceOf<T>>;
|
type ContractInfo<T> = RawContractInfo<CodeHash<T>, BalanceOf<T>>;
|
||||||
|
|
||||||
generate_storage_alias!(
|
#[storage_alias]
|
||||||
Contracts,
|
type ContractInfoOf<T: Config> = StorageMap<
|
||||||
ContractInfoOf<T: Config> => Map<(Twox64Concat, T::AccountId), ContractInfo<T>>
|
Pallet<T>,
|
||||||
);
|
Twox64Concat,
|
||||||
|
<T as frame_system::Config>::AccountId,
|
||||||
|
ContractInfo<T>,
|
||||||
|
>;
|
||||||
|
|
||||||
generate_storage_alias!(
|
#[storage_alias]
|
||||||
Contracts,
|
type CodeStorage<T: Config> = StorageMap<Pallet<T>, Identity, CodeHash<T>, PrefabWasmModule>;
|
||||||
CodeStorage<T: Config> => Map<(Identity, CodeHash<T>), PrefabWasmModule>
|
|
||||||
);
|
|
||||||
|
|
||||||
generate_storage_alias!(
|
#[storage_alias]
|
||||||
Contracts,
|
type OwnerInfoOf<T: Config> = StorageMap<Pallet<T>, Identity, CodeHash<T>, OwnerInfo<T>>;
|
||||||
OwnerInfoOf<T: Config> => Map<(Identity, CodeHash<T>), OwnerInfo<T>>
|
|
||||||
);
|
|
||||||
|
|
||||||
pub fn migrate<T: Config>() -> Weight {
|
pub fn migrate<T: Config>() -> Weight {
|
||||||
let mut weight: Weight = 0;
|
let mut weight: Weight = 0;
|
||||||
@@ -259,15 +256,12 @@ mod v7 {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn migrate<T: Config>() -> Weight {
|
pub fn migrate<T: Config>() -> Weight {
|
||||||
generate_storage_alias!(
|
#[storage_alias]
|
||||||
Contracts,
|
type AccountCounter<T: Config> = StorageValue<Pallet<T>, u64, ValueQuery>;
|
||||||
AccountCounter => Value<u64, ValueQuery>
|
#[storage_alias]
|
||||||
);
|
type Nonce<T: Config> = StorageValue<Pallet<T>, u64, ValueQuery>;
|
||||||
generate_storage_alias!(
|
|
||||||
Contracts,
|
Nonce::<T>::set(AccountCounter::<T>::take());
|
||||||
Nonce => Value<u64, ValueQuery>
|
|
||||||
);
|
|
||||||
Nonce::set(AccountCounter::take());
|
|
||||||
T::DbWeight::get().reads_writes(1, 2)
|
T::DbWeight::get().reads_writes(1, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,10 @@
|
|||||||
|
|
||||||
//! Migrations to version [`3.0.0`], as denoted by the changelog.
|
//! Migrations to version [`3.0.0`], as denoted by the changelog.
|
||||||
|
|
||||||
|
use crate::{Config, Pallet};
|
||||||
use codec::{Decode, Encode, FullCodec};
|
use codec::{Decode, Encode, FullCodec};
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
pallet_prelude::ValueQuery,
|
pallet_prelude::ValueQuery, traits::StorageVersion, weights::Weight, RuntimeDebug, Twox64Concat,
|
||||||
traits::{PalletInfoAccess, StorageVersion},
|
|
||||||
weights::Weight,
|
|
||||||
RuntimeDebug, Twox64Concat,
|
|
||||||
};
|
};
|
||||||
use sp_std::prelude::*;
|
use sp_std::prelude::*;
|
||||||
|
|
||||||
@@ -42,9 +40,6 @@ struct Voter<AccountId, Balance> {
|
|||||||
|
|
||||||
/// Trait to implement to give information about types used for migration
|
/// Trait to implement to give information about types used for migration
|
||||||
pub trait V2ToV3 {
|
pub trait V2ToV3 {
|
||||||
/// The elections-phragmen pallet.
|
|
||||||
type Pallet: 'static + PalletInfoAccess;
|
|
||||||
|
|
||||||
/// System config account id
|
/// System config account id
|
||||||
type AccountId: 'static + FullCodec;
|
type AccountId: 'static + FullCodec;
|
||||||
|
|
||||||
@@ -52,30 +47,31 @@ pub trait V2ToV3 {
|
|||||||
type Balance: 'static + FullCodec + Copy;
|
type Balance: 'static + FullCodec + Copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame_support::generate_storage_alias!(
|
#[frame_support::storage_alias]
|
||||||
PhragmenElection, Candidates<T: V2ToV3> => Value<
|
type Candidates<V, T: Config> =
|
||||||
Vec<(T::AccountId, T::Balance)>,
|
StorageValue<Pallet<T>, Vec<(<V as V2ToV3>::AccountId, <V as V2ToV3>::Balance)>, ValueQuery>;
|
||||||
ValueQuery
|
|
||||||
>
|
#[frame_support::storage_alias]
|
||||||
);
|
type Members<V, T: Config> = StorageValue<
|
||||||
frame_support::generate_storage_alias!(
|
Pallet<T>,
|
||||||
PhragmenElection, Members<T: V2ToV3> => Value<
|
Vec<SeatHolder<<V as V2ToV3>::AccountId, <V as V2ToV3>::Balance>>,
|
||||||
Vec<SeatHolder<T::AccountId, T::Balance>>,
|
ValueQuery,
|
||||||
ValueQuery
|
>;
|
||||||
>
|
|
||||||
);
|
#[frame_support::storage_alias]
|
||||||
frame_support::generate_storage_alias!(
|
type RunnersUp<V, T: Config> = StorageValue<
|
||||||
PhragmenElection, RunnersUp<T: V2ToV3> => Value<
|
Pallet<T>,
|
||||||
Vec<SeatHolder<T::AccountId, T::Balance>>,
|
Vec<SeatHolder<<V as V2ToV3>::AccountId, <V as V2ToV3>::Balance>>,
|
||||||
ValueQuery
|
ValueQuery,
|
||||||
>
|
>;
|
||||||
);
|
|
||||||
frame_support::generate_storage_alias!(
|
#[frame_support::storage_alias]
|
||||||
PhragmenElection, Voting<T: V2ToV3> => Map<
|
type Voting<V, T: Config> = StorageMap<
|
||||||
(Twox64Concat, T::AccountId),
|
Pallet<T>,
|
||||||
Voter<T::AccountId, T::Balance>
|
Twox64Concat,
|
||||||
>
|
<V as V2ToV3>::AccountId,
|
||||||
);
|
Voter<<V as V2ToV3>::AccountId, <V as V2ToV3>::Balance>,
|
||||||
|
>;
|
||||||
|
|
||||||
/// Apply all of the migrations from 2 to 3.
|
/// Apply all of the migrations from 2 to 3.
|
||||||
///
|
///
|
||||||
@@ -86,8 +82,11 @@ frame_support::generate_storage_alias!(
|
|||||||
///
|
///
|
||||||
/// Be aware that this migration is intended to be used only for the mentioned versions. Use
|
/// Be aware that this migration is intended to be used only for the mentioned versions. Use
|
||||||
/// with care and run at your own risk.
|
/// with care and run at your own risk.
|
||||||
pub fn apply<T: V2ToV3>(old_voter_bond: T::Balance, old_candidacy_bond: T::Balance) -> Weight {
|
pub fn apply<V: V2ToV3, T: Config>(
|
||||||
let storage_version = StorageVersion::get::<T::Pallet>();
|
old_voter_bond: V::Balance,
|
||||||
|
old_candidacy_bond: V::Balance,
|
||||||
|
) -> Weight {
|
||||||
|
let storage_version = StorageVersion::get::<Pallet<T>>();
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "runtime::elections-phragmen",
|
target: "runtime::elections-phragmen",
|
||||||
"Running migration for elections-phragmen with storage version {:?}",
|
"Running migration for elections-phragmen with storage version {:?}",
|
||||||
@@ -95,12 +94,12 @@ pub fn apply<T: V2ToV3>(old_voter_bond: T::Balance, old_candidacy_bond: T::Balan
|
|||||||
);
|
);
|
||||||
|
|
||||||
if storage_version <= 2 {
|
if storage_version <= 2 {
|
||||||
migrate_voters_to_recorded_deposit::<T>(old_voter_bond);
|
migrate_voters_to_recorded_deposit::<V, T>(old_voter_bond);
|
||||||
migrate_candidates_to_recorded_deposit::<T>(old_candidacy_bond);
|
migrate_candidates_to_recorded_deposit::<V, T>(old_candidacy_bond);
|
||||||
migrate_runners_up_to_recorded_deposit::<T>(old_candidacy_bond);
|
migrate_runners_up_to_recorded_deposit::<V, T>(old_candidacy_bond);
|
||||||
migrate_members_to_recorded_deposit::<T>(old_candidacy_bond);
|
migrate_members_to_recorded_deposit::<V, T>(old_candidacy_bond);
|
||||||
|
|
||||||
StorageVersion::new(3).put::<T::Pallet>();
|
StorageVersion::new(3).put::<Pallet<T>>();
|
||||||
|
|
||||||
Weight::max_value()
|
Weight::max_value()
|
||||||
} else {
|
} else {
|
||||||
@@ -114,21 +113,21 @@ pub fn apply<T: V2ToV3>(old_voter_bond: T::Balance, old_candidacy_bond: T::Balan
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Migrate from the old legacy voting bond (fixed) to the new one (per-vote dynamic).
|
/// Migrate from the old legacy voting bond (fixed) to the new one (per-vote dynamic).
|
||||||
pub fn migrate_voters_to_recorded_deposit<T: V2ToV3>(old_deposit: T::Balance) {
|
pub fn migrate_voters_to_recorded_deposit<V: V2ToV3, T: Config>(old_deposit: V::Balance) {
|
||||||
<Voting<T>>::translate::<(T::Balance, Vec<T::AccountId>), _>(|_who, (stake, votes)| {
|
<Voting<V, T>>::translate::<(V::Balance, Vec<V::AccountId>), _>(|_who, (stake, votes)| {
|
||||||
Some(Voter { votes, stake, deposit: old_deposit })
|
Some(Voter { votes, stake, deposit: old_deposit })
|
||||||
});
|
});
|
||||||
|
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "runtime::elections-phragmen",
|
target: "runtime::elections-phragmen",
|
||||||
"migrated {} voter accounts.",
|
"migrated {} voter accounts.",
|
||||||
<Voting<T>>::iter().count(),
|
<Voting<V, T>>::iter().count(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Migrate all candidates to recorded deposit.
|
/// Migrate all candidates to recorded deposit.
|
||||||
pub fn migrate_candidates_to_recorded_deposit<T: V2ToV3>(old_deposit: T::Balance) {
|
pub fn migrate_candidates_to_recorded_deposit<V: V2ToV3, T: Config>(old_deposit: V::Balance) {
|
||||||
let _ = <Candidates<T>>::translate::<Vec<T::AccountId>, _>(|maybe_old_candidates| {
|
let _ = <Candidates<V, T>>::translate::<Vec<V::AccountId>, _>(|maybe_old_candidates| {
|
||||||
maybe_old_candidates.map(|old_candidates| {
|
maybe_old_candidates.map(|old_candidates| {
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "runtime::elections-phragmen",
|
target: "runtime::elections-phragmen",
|
||||||
@@ -141,8 +140,8 @@ pub fn migrate_candidates_to_recorded_deposit<T: V2ToV3>(old_deposit: T::Balance
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Migrate all members to recorded deposit.
|
/// Migrate all members to recorded deposit.
|
||||||
pub fn migrate_members_to_recorded_deposit<T: V2ToV3>(old_deposit: T::Balance) {
|
pub fn migrate_members_to_recorded_deposit<V: V2ToV3, T: Config>(old_deposit: V::Balance) {
|
||||||
let _ = <Members<T>>::translate::<Vec<(T::AccountId, T::Balance)>, _>(|maybe_old_members| {
|
let _ = <Members<V, T>>::translate::<Vec<(V::AccountId, V::Balance)>, _>(|maybe_old_members| {
|
||||||
maybe_old_members.map(|old_members| {
|
maybe_old_members.map(|old_members| {
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "runtime::elections-phragmen",
|
target: "runtime::elections-phragmen",
|
||||||
@@ -158,9 +157,9 @@ pub fn migrate_members_to_recorded_deposit<T: V2ToV3>(old_deposit: T::Balance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Migrate all runners-up to recorded deposit.
|
/// Migrate all runners-up to recorded deposit.
|
||||||
pub fn migrate_runners_up_to_recorded_deposit<T: V2ToV3>(old_deposit: T::Balance) {
|
pub fn migrate_runners_up_to_recorded_deposit<V: V2ToV3, T: Config>(old_deposit: V::Balance) {
|
||||||
let _ =
|
let _ = <RunnersUp<V, T>>::translate::<Vec<(V::AccountId, V::Balance)>, _>(
|
||||||
<RunnersUp<T>>::translate::<Vec<(T::AccountId, T::Balance)>, _>(|maybe_old_runners_up| {
|
|maybe_old_runners_up| {
|
||||||
maybe_old_runners_up.map(|old_runners_up| {
|
maybe_old_runners_up.map(|old_runners_up| {
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "runtime::elections-phragmen",
|
target: "runtime::elections-phragmen",
|
||||||
@@ -172,5 +171,6 @@ pub fn migrate_runners_up_to_recorded_deposit<T: V2ToV3>(old_deposit: T::Balance
|
|||||||
.map(|(who, stake)| SeatHolder { who, stake, deposit: old_deposit })
|
.map(|(who, stake)| SeatHolder { who, stake, deposit: old_deposit })
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use super::{Config, OffenceDetails, Perbill, SessionIndex};
|
use super::{Config, OffenceDetails, Perbill, SessionIndex};
|
||||||
use frame_support::{
|
use frame_support::{pallet_prelude::ValueQuery, storage_alias, traits::Get, weights::Weight};
|
||||||
generate_storage_alias, pallet_prelude::ValueQuery, traits::Get, weights::Weight,
|
|
||||||
};
|
|
||||||
use sp_staking::offence::{DisableStrategy, OnOffenceHandler};
|
use sp_staking::offence::{DisableStrategy, OnOffenceHandler};
|
||||||
use sp_std::vec::Vec;
|
use sp_std::vec::Vec;
|
||||||
|
|
||||||
@@ -31,10 +29,9 @@ type DeferredOffenceOf<T> = (
|
|||||||
|
|
||||||
// Deferred reports that have been rejected by the offence handler and need to be submitted
|
// Deferred reports that have been rejected by the offence handler and need to be submitted
|
||||||
// at a later time.
|
// at a later time.
|
||||||
generate_storage_alias!(
|
#[storage_alias]
|
||||||
Offences,
|
type DeferredOffences<T: Config> =
|
||||||
DeferredOffences<T: Config> => Value<Vec<DeferredOffenceOf<T>>, ValueQuery>
|
StorageValue<crate::Pallet<T>, Vec<DeferredOffenceOf<T>>, ValueQuery>;
|
||||||
);
|
|
||||||
|
|
||||||
pub fn remove_deferred_storage<T: Config>() -> Weight {
|
pub fn remove_deferred_storage<T: Config>() -> Weight {
|
||||||
let mut weight = T::DbWeight::get().reads_writes(1, 1);
|
let mut weight = T::DbWeight::get().reads_writes(1, 1);
|
||||||
|
|||||||
@@ -139,18 +139,20 @@ pub mod v8 {
|
|||||||
|
|
||||||
pub mod v7 {
|
pub mod v7 {
|
||||||
use super::*;
|
use super::*;
|
||||||
use frame_support::generate_storage_alias;
|
use frame_support::storage_alias;
|
||||||
|
|
||||||
generate_storage_alias!(Staking, CounterForValidators => Value<u32>);
|
#[storage_alias]
|
||||||
generate_storage_alias!(Staking, CounterForNominators => Value<u32>);
|
type CounterForValidators<T: Config> = StorageValue<Pallet<T>, u32>;
|
||||||
|
#[storage_alias]
|
||||||
|
type CounterForNominators<T: Config> = StorageValue<Pallet<T>, u32>;
|
||||||
|
|
||||||
pub fn pre_migrate<T: Config>() -> Result<(), &'static str> {
|
pub fn pre_migrate<T: Config>() -> Result<(), &'static str> {
|
||||||
assert!(
|
assert!(
|
||||||
CounterForValidators::get().unwrap().is_zero(),
|
CounterForValidators::<T>::get().unwrap().is_zero(),
|
||||||
"CounterForValidators already set."
|
"CounterForValidators already set."
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
CounterForNominators::get().unwrap().is_zero(),
|
CounterForNominators::<T>::get().unwrap().is_zero(),
|
||||||
"CounterForNominators already set."
|
"CounterForNominators already set."
|
||||||
);
|
);
|
||||||
assert!(Validators::<T>::count().is_zero(), "Validators already set.");
|
assert!(Validators::<T>::count().is_zero(), "Validators already set.");
|
||||||
@@ -164,8 +166,8 @@ pub mod v7 {
|
|||||||
let validator_count = Validators::<T>::iter().count() as u32;
|
let validator_count = Validators::<T>::iter().count() as u32;
|
||||||
let nominator_count = Nominators::<T>::iter().count() as u32;
|
let nominator_count = Nominators::<T>::iter().count() as u32;
|
||||||
|
|
||||||
CounterForValidators::put(validator_count);
|
CounterForValidators::<T>::put(validator_count);
|
||||||
CounterForNominators::put(nominator_count);
|
CounterForNominators::<T>::put(nominator_count);
|
||||||
|
|
||||||
StorageVersion::<T>::put(Releases::V7_0_0);
|
StorageVersion::<T>::put(Releases::V7_0_0);
|
||||||
log!(info, "Completed staking migration to Releases::V7_0_0");
|
log!(info, "Completed staking migration to Releases::V7_0_0");
|
||||||
@@ -176,26 +178,35 @@ pub mod v7 {
|
|||||||
|
|
||||||
pub mod v6 {
|
pub mod v6 {
|
||||||
use super::*;
|
use super::*;
|
||||||
use frame_support::{generate_storage_alias, traits::Get, weights::Weight};
|
use frame_support::{storage_alias, traits::Get, weights::Weight};
|
||||||
|
|
||||||
// NOTE: value type doesn't matter, we just set it to () here.
|
// NOTE: value type doesn't matter, we just set it to () here.
|
||||||
generate_storage_alias!(Staking, SnapshotValidators => Value<()>);
|
#[storage_alias]
|
||||||
generate_storage_alias!(Staking, SnapshotNominators => Value<()>);
|
type SnapshotValidators<T: Config> = StorageValue<Pallet<T>, ()>;
|
||||||
generate_storage_alias!(Staking, QueuedElected => Value<()>);
|
#[storage_alias]
|
||||||
generate_storage_alias!(Staking, QueuedScore => Value<()>);
|
type SnapshotNominators<T: Config> = StorageValue<Pallet<T>, ()>;
|
||||||
generate_storage_alias!(Staking, EraElectionStatus => Value<()>);
|
#[storage_alias]
|
||||||
generate_storage_alias!(Staking, IsCurrentSessionFinal => Value<()>);
|
type QueuedElected<T: Config> = StorageValue<Pallet<T>, ()>;
|
||||||
|
#[storage_alias]
|
||||||
|
type QueuedScore<T: Config> = StorageValue<Pallet<T>, ()>;
|
||||||
|
#[storage_alias]
|
||||||
|
type EraElectionStatus<T: Config> = StorageValue<Pallet<T>, ()>;
|
||||||
|
#[storage_alias]
|
||||||
|
type IsCurrentSessionFinal<T: Config> = StorageValue<Pallet<T>, ()>;
|
||||||
|
|
||||||
/// check to execute prior to migration.
|
/// check to execute prior to migration.
|
||||||
pub fn pre_migrate<T: Config>() -> Result<(), &'static str> {
|
pub fn pre_migrate<T: Config>() -> Result<(), &'static str> {
|
||||||
// these may or may not exist.
|
// these may or may not exist.
|
||||||
log!(info, "SnapshotValidators.exits()? {:?}", SnapshotValidators::exists());
|
log!(info, "SnapshotValidators.exits()? {:?}", SnapshotValidators::<T>::exists());
|
||||||
log!(info, "SnapshotNominators.exits()? {:?}", SnapshotNominators::exists());
|
log!(info, "SnapshotNominators.exits()? {:?}", SnapshotNominators::<T>::exists());
|
||||||
log!(info, "QueuedElected.exits()? {:?}", QueuedElected::exists());
|
log!(info, "QueuedElected.exits()? {:?}", QueuedElected::<T>::exists());
|
||||||
log!(info, "QueuedScore.exits()? {:?}", QueuedScore::exists());
|
log!(info, "QueuedScore.exits()? {:?}", QueuedScore::<T>::exists());
|
||||||
// these must exist.
|
// these must exist.
|
||||||
assert!(IsCurrentSessionFinal::exists(), "IsCurrentSessionFinal storage item not found!");
|
assert!(
|
||||||
assert!(EraElectionStatus::exists(), "EraElectionStatus storage item not found!");
|
IsCurrentSessionFinal::<T>::exists(),
|
||||||
|
"IsCurrentSessionFinal storage item not found!"
|
||||||
|
);
|
||||||
|
assert!(EraElectionStatus::<T>::exists(), "EraElectionStatus storage item not found!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,12 +214,12 @@ pub mod v6 {
|
|||||||
pub fn migrate<T: Config>() -> Weight {
|
pub fn migrate<T: Config>() -> Weight {
|
||||||
log!(info, "Migrating staking to Releases::V6_0_0");
|
log!(info, "Migrating staking to Releases::V6_0_0");
|
||||||
|
|
||||||
SnapshotValidators::kill();
|
SnapshotValidators::<T>::kill();
|
||||||
SnapshotNominators::kill();
|
SnapshotNominators::<T>::kill();
|
||||||
QueuedElected::kill();
|
QueuedElected::<T>::kill();
|
||||||
QueuedScore::kill();
|
QueuedScore::<T>::kill();
|
||||||
EraElectionStatus::kill();
|
EraElectionStatus::<T>::kill();
|
||||||
IsCurrentSessionFinal::kill();
|
IsCurrentSessionFinal::<T>::kill();
|
||||||
|
|
||||||
StorageVersion::<T>::put(Releases::V6_0_0);
|
StorageVersion::<T>::put(Releases::V6_0_0);
|
||||||
log!(info, "Done.");
|
log!(info, "Done.");
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ mod pallet;
|
|||||||
mod pallet_error;
|
mod pallet_error;
|
||||||
mod partial_eq_no_bound;
|
mod partial_eq_no_bound;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
mod storage_alias;
|
||||||
mod transactional;
|
mod transactional;
|
||||||
mod tt_macro;
|
mod tt_macro;
|
||||||
|
|
||||||
@@ -575,3 +576,10 @@ pub fn derive_pallet_error(input: TokenStream) -> TokenStream {
|
|||||||
pub fn __create_tt_macro(input: TokenStream) -> TokenStream {
|
pub fn __create_tt_macro(input: TokenStream) -> TokenStream {
|
||||||
tt_macro::create_tt_return_macro(input)
|
tt_macro::create_tt_return_macro(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn storage_alias(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
storage_alias::storage_alias(input.into())
|
||||||
|
.unwrap_or_else(|r| r.into_compile_error())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,566 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Implementation of the `storage_alias` attribute macro.
|
||||||
|
|
||||||
|
use frame_support_procedural_tools::generate_crate_access_2018;
|
||||||
|
use proc_macro2::{Span, TokenStream};
|
||||||
|
use quote::{quote, ToTokens};
|
||||||
|
use syn::{
|
||||||
|
ext::IdentExt,
|
||||||
|
parenthesized,
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
punctuated::Punctuated,
|
||||||
|
token, Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents a path that only consists of [`Ident`] separated by `::`.
|
||||||
|
struct SimplePath {
|
||||||
|
leading_colon: Option<Token![::]>,
|
||||||
|
segments: Punctuated<Ident, Token![::]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimplePath {
|
||||||
|
/// Returns the [`Ident`] of this path.
|
||||||
|
///
|
||||||
|
/// It only returns `Some(_)` if there is exactly one element and no leading colon.
|
||||||
|
fn get_ident(&self) -> Option<&Ident> {
|
||||||
|
if self.segments.len() != 1 || self.leading_colon.is_some() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.segments.first()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for SimplePath {
|
||||||
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
leading_colon: if input.peek(Token![::]) { Some(input.parse()?) } else { None },
|
||||||
|
segments: Punctuated::parse_separated_nonempty_with(input, |p| Ident::parse_any(p))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for SimplePath {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
self.leading_colon.to_tokens(tokens);
|
||||||
|
self.segments.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents generics which only support [`Ident`] separated by commas as you would pass it to a
|
||||||
|
/// type.
|
||||||
|
struct TypeGenerics {
|
||||||
|
lt_token: Token![<],
|
||||||
|
params: Punctuated<Ident, token::Comma>,
|
||||||
|
gt_token: Token![>],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeGenerics {
|
||||||
|
/// Returns the generics for types declarations etc.
|
||||||
|
fn iter(&self) -> impl Iterator<Item = &Ident> {
|
||||||
|
self.params.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for TypeGenerics {
|
||||||
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
lt_token: input.parse()?,
|
||||||
|
params: Punctuated::parse_separated_nonempty(input)?,
|
||||||
|
gt_token: input.parse()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for TypeGenerics {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
self.lt_token.to_tokens(tokens);
|
||||||
|
self.params.to_tokens(tokens);
|
||||||
|
self.gt_token.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents generics which only support [`TypeParam`] separated by commas.
|
||||||
|
struct SimpleGenerics {
|
||||||
|
lt_token: Token![<],
|
||||||
|
params: Punctuated<TypeParam, token::Comma>,
|
||||||
|
gt_token: Token![>],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleGenerics {
|
||||||
|
/// Returns the generics for types declarations etc.
|
||||||
|
fn type_generics(&self) -> impl Iterator<Item = &Ident> {
|
||||||
|
self.params.iter().map(|p| &p.ident)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the generics for the `impl` block.
|
||||||
|
fn impl_generics(&self) -> impl Iterator<Item = &TypeParam> {
|
||||||
|
self.params.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for SimpleGenerics {
|
||||||
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
lt_token: input.parse()?,
|
||||||
|
params: Punctuated::parse_separated_nonempty(input)?,
|
||||||
|
gt_token: input.parse()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for SimpleGenerics {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
self.lt_token.to_tokens(tokens);
|
||||||
|
self.params.to_tokens(tokens);
|
||||||
|
self.gt_token.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod storage_types {
|
||||||
|
syn::custom_keyword!(StorageValue);
|
||||||
|
syn::custom_keyword!(StorageMap);
|
||||||
|
syn::custom_keyword!(StorageDoubleMap);
|
||||||
|
syn::custom_keyword!(StorageNMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The supported storage types
|
||||||
|
enum StorageType {
|
||||||
|
Value {
|
||||||
|
_kw: storage_types::StorageValue,
|
||||||
|
_lt_token: Token![<],
|
||||||
|
prefix: SimplePath,
|
||||||
|
prefix_generics: Option<TypeGenerics>,
|
||||||
|
_value_comma: Token![,],
|
||||||
|
value_ty: Type,
|
||||||
|
query_type: Option<(Token![,], Type)>,
|
||||||
|
_trailing_comma: Option<Token![,]>,
|
||||||
|
_gt_token: Token![>],
|
||||||
|
},
|
||||||
|
Map {
|
||||||
|
_kw: storage_types::StorageMap,
|
||||||
|
_lt_token: Token![<],
|
||||||
|
prefix: SimplePath,
|
||||||
|
prefix_generics: Option<TypeGenerics>,
|
||||||
|
_hasher_comma: Token![,],
|
||||||
|
hasher_ty: Type,
|
||||||
|
_key_comma: Token![,],
|
||||||
|
key_ty: Type,
|
||||||
|
_value_comma: Token![,],
|
||||||
|
value_ty: Type,
|
||||||
|
query_type: Option<(Token![,], Type)>,
|
||||||
|
_trailing_comma: Option<Token![,]>,
|
||||||
|
_gt_token: Token![>],
|
||||||
|
},
|
||||||
|
DoubleMap {
|
||||||
|
_kw: storage_types::StorageDoubleMap,
|
||||||
|
_lt_token: Token![<],
|
||||||
|
prefix: SimplePath,
|
||||||
|
prefix_generics: Option<TypeGenerics>,
|
||||||
|
_hasher1_comma: Token![,],
|
||||||
|
hasher1_ty: Type,
|
||||||
|
_key1_comma: Token![,],
|
||||||
|
key1_ty: Type,
|
||||||
|
_hasher2_comma: Token![,],
|
||||||
|
hasher2_ty: Type,
|
||||||
|
_key2_comma: Token![,],
|
||||||
|
key2_ty: Type,
|
||||||
|
_value_comma: Token![,],
|
||||||
|
value_ty: Type,
|
||||||
|
query_type: Option<(Token![,], Type)>,
|
||||||
|
_trailing_comma: Option<Token![,]>,
|
||||||
|
_gt_token: Token![>],
|
||||||
|
},
|
||||||
|
NMap {
|
||||||
|
_kw: storage_types::StorageNMap,
|
||||||
|
_lt_token: Token![<],
|
||||||
|
prefix: SimplePath,
|
||||||
|
prefix_generics: Option<TypeGenerics>,
|
||||||
|
_paren_comma: Token![,],
|
||||||
|
_paren_token: token::Paren,
|
||||||
|
key_types: Punctuated<Type, Token![,]>,
|
||||||
|
_value_comma: Token![,],
|
||||||
|
value_ty: Type,
|
||||||
|
query_type: Option<(Token![,], Type)>,
|
||||||
|
_trailing_comma: Option<Token![,]>,
|
||||||
|
_gt_token: Token![>],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StorageType {
|
||||||
|
/// Generate the actual type declaration.
|
||||||
|
fn generate_type_declaration(
|
||||||
|
&self,
|
||||||
|
crate_: &Ident,
|
||||||
|
storage_instance: &StorageInstance,
|
||||||
|
storage_name: &Ident,
|
||||||
|
storage_generics: Option<&SimpleGenerics>,
|
||||||
|
visibility: &Visibility,
|
||||||
|
attributes: &[Attribute],
|
||||||
|
) -> TokenStream {
|
||||||
|
let storage_instance = &storage_instance.name;
|
||||||
|
let attributes = attributes.iter();
|
||||||
|
let storage_generics = storage_generics.map(|g| {
|
||||||
|
let generics = g.type_generics();
|
||||||
|
|
||||||
|
quote!( < #( #generics ),* > )
|
||||||
|
});
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Value { value_ty, query_type, prefix_generics, .. } => {
|
||||||
|
let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#( #attributes )*
|
||||||
|
#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue<
|
||||||
|
#storage_instance #prefix_generics,
|
||||||
|
#value_ty
|
||||||
|
#query_type
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::Map { value_ty, query_type, hasher_ty, key_ty, prefix_generics, .. } => {
|
||||||
|
let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#( #attributes )*
|
||||||
|
#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageMap<
|
||||||
|
#storage_instance #prefix_generics,
|
||||||
|
#hasher_ty,
|
||||||
|
#key_ty,
|
||||||
|
#value_ty
|
||||||
|
#query_type
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::DoubleMap {
|
||||||
|
value_ty,
|
||||||
|
query_type,
|
||||||
|
hasher1_ty,
|
||||||
|
key1_ty,
|
||||||
|
hasher2_ty,
|
||||||
|
key2_ty,
|
||||||
|
prefix_generics,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#( #attributes )*
|
||||||
|
#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap<
|
||||||
|
#storage_instance #prefix_generics,
|
||||||
|
#hasher1_ty,
|
||||||
|
#key1_ty,
|
||||||
|
#hasher2_ty,
|
||||||
|
#key2_ty,
|
||||||
|
#value_ty
|
||||||
|
#query_type
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::NMap { value_ty, query_type, key_types, prefix_generics, .. } => {
|
||||||
|
let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
|
||||||
|
let key_types = key_types.iter();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#( #attributes )*
|
||||||
|
#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap<
|
||||||
|
#storage_instance #prefix_generics,
|
||||||
|
( #( #key_types ),* ),
|
||||||
|
#value_ty
|
||||||
|
#query_type
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The prefix for this storage type.
|
||||||
|
fn prefix(&self) -> &SimplePath {
|
||||||
|
match self {
|
||||||
|
Self::Value { prefix, .. } |
|
||||||
|
Self::Map { prefix, .. } |
|
||||||
|
Self::NMap { prefix, .. } |
|
||||||
|
Self::DoubleMap { prefix, .. } => prefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The prefix generics for this storage type.
|
||||||
|
fn prefix_generics(&self) -> Option<&TypeGenerics> {
|
||||||
|
match self {
|
||||||
|
Self::Value { prefix_generics, .. } |
|
||||||
|
Self::Map { prefix_generics, .. } |
|
||||||
|
Self::NMap { prefix_generics, .. } |
|
||||||
|
Self::DoubleMap { prefix_generics, .. } => prefix_generics.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for StorageType {
|
||||||
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
|
let lookahead = input.lookahead1();
|
||||||
|
|
||||||
|
let parse_query_type = |input: ParseStream<'_>| -> Result<Option<(Token![,], Type)>> {
|
||||||
|
if input.peek(Token![,]) && !input.peek2(Token![>]) {
|
||||||
|
Ok(Some((input.parse()?, input.parse()?)))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let parse_pallet_generics = |input: ParseStream<'_>| -> Result<Option<TypeGenerics>> {
|
||||||
|
let lookahead = input.lookahead1();
|
||||||
|
if lookahead.peek(Token![<]) {
|
||||||
|
Ok(Some(input.parse()?))
|
||||||
|
} else if lookahead.peek(Token![,]) {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Err(lookahead.error())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if lookahead.peek(storage_types::StorageValue) {
|
||||||
|
Ok(Self::Value {
|
||||||
|
_kw: input.parse()?,
|
||||||
|
_lt_token: input.parse()?,
|
||||||
|
prefix: input.parse()?,
|
||||||
|
prefix_generics: parse_pallet_generics(input)?,
|
||||||
|
_value_comma: input.parse()?,
|
||||||
|
value_ty: input.parse()?,
|
||||||
|
query_type: parse_query_type(input)?,
|
||||||
|
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
|
||||||
|
_gt_token: input.parse()?,
|
||||||
|
})
|
||||||
|
} else if lookahead.peek(storage_types::StorageMap) {
|
||||||
|
Ok(Self::Map {
|
||||||
|
_kw: input.parse()?,
|
||||||
|
_lt_token: input.parse()?,
|
||||||
|
prefix: input.parse()?,
|
||||||
|
prefix_generics: parse_pallet_generics(input)?,
|
||||||
|
_hasher_comma: input.parse()?,
|
||||||
|
hasher_ty: input.parse()?,
|
||||||
|
_key_comma: input.parse()?,
|
||||||
|
key_ty: input.parse()?,
|
||||||
|
_value_comma: input.parse()?,
|
||||||
|
value_ty: input.parse()?,
|
||||||
|
query_type: parse_query_type(input)?,
|
||||||
|
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
|
||||||
|
_gt_token: input.parse()?,
|
||||||
|
})
|
||||||
|
} else if lookahead.peek(storage_types::StorageDoubleMap) {
|
||||||
|
Ok(Self::DoubleMap {
|
||||||
|
_kw: input.parse()?,
|
||||||
|
_lt_token: input.parse()?,
|
||||||
|
prefix: input.parse()?,
|
||||||
|
prefix_generics: parse_pallet_generics(input)?,
|
||||||
|
_hasher1_comma: input.parse()?,
|
||||||
|
hasher1_ty: input.parse()?,
|
||||||
|
_key1_comma: input.parse()?,
|
||||||
|
key1_ty: input.parse()?,
|
||||||
|
_hasher2_comma: input.parse()?,
|
||||||
|
hasher2_ty: input.parse()?,
|
||||||
|
_key2_comma: input.parse()?,
|
||||||
|
key2_ty: input.parse()?,
|
||||||
|
_value_comma: input.parse()?,
|
||||||
|
value_ty: input.parse()?,
|
||||||
|
query_type: parse_query_type(input)?,
|
||||||
|
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
|
||||||
|
_gt_token: input.parse()?,
|
||||||
|
})
|
||||||
|
} else if lookahead.peek(storage_types::StorageNMap) {
|
||||||
|
let content;
|
||||||
|
Ok(Self::NMap {
|
||||||
|
_kw: input.parse()?,
|
||||||
|
_lt_token: input.parse()?,
|
||||||
|
prefix: input.parse()?,
|
||||||
|
prefix_generics: parse_pallet_generics(input)?,
|
||||||
|
_paren_comma: input.parse()?,
|
||||||
|
_paren_token: parenthesized!(content in input),
|
||||||
|
key_types: Punctuated::parse_terminated(&content)?,
|
||||||
|
_value_comma: input.parse()?,
|
||||||
|
value_ty: input.parse()?,
|
||||||
|
query_type: parse_query_type(input)?,
|
||||||
|
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
|
||||||
|
_gt_token: input.parse()?,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(lookahead.error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The input expected by this macro.
|
||||||
|
struct Input {
|
||||||
|
attributes: Vec<Attribute>,
|
||||||
|
visibility: Visibility,
|
||||||
|
_type: Token![type],
|
||||||
|
storage_name: Ident,
|
||||||
|
storage_generics: Option<SimpleGenerics>,
|
||||||
|
where_clause: Option<WhereClause>,
|
||||||
|
_equal: Token![=],
|
||||||
|
storage_type: StorageType,
|
||||||
|
_semicolon: Token![;],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Input {
|
||||||
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
|
let attributes = input.call(Attribute::parse_outer)?;
|
||||||
|
let visibility = input.parse()?;
|
||||||
|
let _type = input.parse()?;
|
||||||
|
let storage_name = input.parse()?;
|
||||||
|
|
||||||
|
let lookahead = input.lookahead1();
|
||||||
|
let storage_generics = if lookahead.peek(Token![<]) {
|
||||||
|
Some(input.parse()?)
|
||||||
|
} else if lookahead.peek(Token![=]) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
return Err(lookahead.error())
|
||||||
|
};
|
||||||
|
|
||||||
|
let lookahead = input.lookahead1();
|
||||||
|
let where_clause = if lookahead.peek(Token![where]) {
|
||||||
|
Some(input.parse()?)
|
||||||
|
} else if lookahead.peek(Token![=]) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
return Err(lookahead.error())
|
||||||
|
};
|
||||||
|
|
||||||
|
let _equal = input.parse()?;
|
||||||
|
|
||||||
|
let storage_type = input.parse()?;
|
||||||
|
|
||||||
|
let _semicolon = input.parse()?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
attributes,
|
||||||
|
visibility,
|
||||||
|
_type,
|
||||||
|
storage_name,
|
||||||
|
storage_generics,
|
||||||
|
_equal,
|
||||||
|
storage_type,
|
||||||
|
where_clause,
|
||||||
|
_semicolon,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of the `storage_alias` attribute macro.
|
||||||
|
pub fn storage_alias(input: TokenStream) -> Result<TokenStream> {
|
||||||
|
let input = syn::parse2::<Input>(input)?;
|
||||||
|
let crate_ = generate_crate_access_2018("frame-support")?;
|
||||||
|
|
||||||
|
let storage_instance = generate_storage_instance(
|
||||||
|
&crate_,
|
||||||
|
&input.storage_name,
|
||||||
|
input.storage_generics.as_ref(),
|
||||||
|
input.where_clause.as_ref(),
|
||||||
|
input.storage_type.prefix(),
|
||||||
|
input.storage_type.prefix_generics(),
|
||||||
|
&input.visibility,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let definition = input.storage_type.generate_type_declaration(
|
||||||
|
&crate_,
|
||||||
|
&storage_instance,
|
||||||
|
&input.storage_name,
|
||||||
|
input.storage_generics.as_ref(),
|
||||||
|
&input.visibility,
|
||||||
|
&input.attributes,
|
||||||
|
);
|
||||||
|
|
||||||
|
let storage_instance_code = storage_instance.code;
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
#storage_instance_code
|
||||||
|
|
||||||
|
#definition
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The storage instance to use for the storage alias.
|
||||||
|
struct StorageInstance {
|
||||||
|
name: Ident,
|
||||||
|
code: TokenStream,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate the [`StorageInstance`] for the storage alias.
|
||||||
|
fn generate_storage_instance(
|
||||||
|
crate_: &Ident,
|
||||||
|
storage_name: &Ident,
|
||||||
|
storage_generics: Option<&SimpleGenerics>,
|
||||||
|
storage_where_clause: Option<&WhereClause>,
|
||||||
|
prefix: &SimplePath,
|
||||||
|
prefix_generics: Option<&TypeGenerics>,
|
||||||
|
visibility: &Visibility,
|
||||||
|
) -> Result<StorageInstance> {
|
||||||
|
let (pallet_prefix, impl_generics, type_generics) =
|
||||||
|
if let Some((prefix_generics, storage_generics)) =
|
||||||
|
prefix_generics.and_then(|p| storage_generics.map(|s| (p, s)))
|
||||||
|
{
|
||||||
|
let type_generics = prefix_generics.iter();
|
||||||
|
let type_generics2 = prefix_generics.iter();
|
||||||
|
let impl_generics = storage_generics
|
||||||
|
.impl_generics()
|
||||||
|
.filter(|g| prefix_generics.params.iter().any(|pg| *pg == g.ident));
|
||||||
|
|
||||||
|
(
|
||||||
|
quote! {
|
||||||
|
<#prefix < #( #type_generics2 ),* > as #crate_::traits::PalletInfoAccess>::name()
|
||||||
|
},
|
||||||
|
quote!( #( #impl_generics ),* ),
|
||||||
|
quote!( #( #type_generics ),* ),
|
||||||
|
)
|
||||||
|
} else if let Some(prefix) = prefix.get_ident() {
|
||||||
|
let prefix_str = prefix.to_string();
|
||||||
|
|
||||||
|
(quote!(#prefix_str), quote!(), quote!())
|
||||||
|
} else {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
prefix,
|
||||||
|
"If there are no generics, the prefix is only allowed to be an identifier.",
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default();
|
||||||
|
|
||||||
|
let name = Ident::new(&format!("{}_Storage_Instance", storage_name), Span::call_site());
|
||||||
|
let storage_name_str = storage_name.to_string();
|
||||||
|
|
||||||
|
// Implement `StorageInstance` trait.
|
||||||
|
let code = quote! {
|
||||||
|
#visibility struct #name< #impl_generics >(
|
||||||
|
#crate_::sp_std::marker::PhantomData<(#type_generics)>
|
||||||
|
) #where_clause;
|
||||||
|
|
||||||
|
impl<#impl_generics> #crate_::traits::StorageInstance
|
||||||
|
for #name< #type_generics > #where_clause
|
||||||
|
{
|
||||||
|
fn pallet_prefix() -> &'static str {
|
||||||
|
#pallet_prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
const STORAGE_PREFIX: &'static str = #storage_name_str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(StorageInstance { name, code })
|
||||||
|
}
|
||||||
@@ -166,178 +166,74 @@ macro_rules! bounded_btree_map {
|
|||||||
/// Useful for creating a *storage-like* struct for test and migrations.
|
/// Useful for creating a *storage-like* struct for test and migrations.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use frame_support::generate_storage_alias;
|
/// # use frame_support::storage_alias;
|
||||||
/// use frame_support::codec;
|
/// use frame_support::codec;
|
||||||
/// use frame_support::Twox64Concat;
|
/// use frame_support::Twox64Concat;
|
||||||
/// // generate a storage value with type u32.
|
/// // generate a storage value with type u32.
|
||||||
/// generate_storage_alias!(Prefix, StorageName => Value<u32>);
|
/// #[storage_alias]
|
||||||
|
/// type StorageName = StorageValue<Prefix, u32>;
|
||||||
///
|
///
|
||||||
/// // generate a double map from `(u32, u32)` (with hashers `Twox64Concat` for each key)
|
/// // generate a double map from `(u32, u32)` (with hashers `Twox64Concat` for each key)
|
||||||
/// // to `Vec<u8>`
|
/// // to `Vec<u8>`
|
||||||
/// generate_storage_alias!(
|
/// #[storage_alias]
|
||||||
/// OtherPrefix, OtherStorageName => DoubleMap<
|
/// type OtherStorageName = StorageDoubleMap<
|
||||||
/// (Twox64Concat, u32),
|
/// OtherPrefix,
|
||||||
/// (Twox64Concat, u32),
|
/// Twox64Concat,
|
||||||
/// Vec<u8>
|
/// u32,
|
||||||
/// >
|
/// Twox64Concat,
|
||||||
/// );
|
/// u32,
|
||||||
|
/// Vec<u8>,
|
||||||
|
/// >;
|
||||||
///
|
///
|
||||||
/// // optionally specify the query type
|
/// // optionally specify the query type
|
||||||
/// use frame_support::pallet_prelude::{ValueQuery, OptionQuery};
|
/// use frame_support::pallet_prelude::{ValueQuery, OptionQuery};
|
||||||
/// generate_storage_alias!(Prefix, ValueName => Value<u32, OptionQuery>);
|
/// #[storage_alias]
|
||||||
/// generate_storage_alias!(
|
/// type ValueName = StorageValue<Prefix, u32, OptionQuery>;
|
||||||
/// Prefix, SomeStorageName => DoubleMap<
|
/// #[storage_alias]
|
||||||
/// (Twox64Concat, u32),
|
/// type SomeStorageName = StorageMap<
|
||||||
/// (Twox64Concat, u32),
|
/// Prefix,
|
||||||
/// Vec<u8>,
|
/// Twox64Concat,
|
||||||
/// ValueQuery
|
/// u32,
|
||||||
/// >
|
/// Vec<u8>,
|
||||||
/// );
|
/// ValueQuery,
|
||||||
|
/// >;
|
||||||
///
|
///
|
||||||
/// // generate a map from `Config::AccountId` (with hasher `Twox64Concat`) to `Vec<u8>`
|
/// // generate a map from `Config::AccountId` (with hasher `Twox64Concat`) to `Vec<u8>`
|
||||||
/// trait Config { type AccountId: codec::FullCodec; }
|
/// trait Config { type AccountId: codec::FullCodec; }
|
||||||
/// generate_storage_alias!(
|
/// #[storage_alias]
|
||||||
/// Prefix, GenericStorage<T: Config> => Map<(Twox64Concat, T::AccountId), Vec<u8>>
|
/// type GenericStorage<T> = StorageMap<Prefix, Twox64Concat, <T as Config>::AccountId, Vec<u8>>;
|
||||||
/// );
|
///
|
||||||
|
/// // It also supports NMap
|
||||||
|
/// use frame_support::storage::types::Key as NMapKey;
|
||||||
|
///
|
||||||
|
/// #[storage_alias]
|
||||||
|
/// type SomeNMap = StorageNMap<Prefix, (NMapKey<Twox64Concat, u32>, NMapKey<Twox64Concat, u64>), Vec<u8>>;
|
||||||
|
///
|
||||||
|
/// // Using pallet name as prefix.
|
||||||
|
/// //
|
||||||
|
/// // When the first generic argument is taking generic arguments it is expected to be a pallet.
|
||||||
|
/// // The prefix will then be the pallet name as configured in the runtime through
|
||||||
|
/// // `construct_runtime!`.
|
||||||
|
///
|
||||||
|
/// # struct Pallet<T: Config, I = ()>(std::marker::PhantomData<(T, I)>);
|
||||||
|
/// # impl<T: Config, I: 'static> frame_support::traits::PalletInfoAccess for Pallet<T, I> {
|
||||||
|
/// # fn index() -> usize { 0 }
|
||||||
|
/// # fn name() -> &'static str { "pallet" }
|
||||||
|
/// # fn module_name() -> &'static str { "module" }
|
||||||
|
/// # fn crate_version() -> frame_support::traits::CrateVersion { unimplemented!() }
|
||||||
|
/// # }
|
||||||
|
///
|
||||||
|
/// #[storage_alias]
|
||||||
|
/// type SomeValue<T: Config> = StorageValue<Pallet<T>, u64>;
|
||||||
|
///
|
||||||
|
/// // Pallet with instance
|
||||||
|
///
|
||||||
|
/// #[storage_alias]
|
||||||
|
/// type SomeValue2<T: Config, I: 'static> = StorageValue<Pallet<T, I>, u64>;
|
||||||
|
///
|
||||||
/// # fn main() {}
|
/// # fn main() {}
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
pub use frame_support_procedural::storage_alias;
|
||||||
macro_rules! generate_storage_alias {
|
|
||||||
// without generic for $name.
|
|
||||||
($pallet:ident, $name:ident => Map<($hasher:ty, $key:ty), $value:ty $(, $querytype:ty)?>) => {
|
|
||||||
$crate::paste::paste! {
|
|
||||||
$crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name);
|
|
||||||
type $name = $crate::storage::types::StorageMap<
|
|
||||||
[<$name Instance>],
|
|
||||||
$hasher,
|
|
||||||
$key,
|
|
||||||
$value,
|
|
||||||
$( $querytype )?
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(
|
|
||||||
$pallet:ident,
|
|
||||||
$name:ident
|
|
||||||
=> DoubleMap<($hasher1:ty, $key1:ty), ($hasher2:ty, $key2:ty), $value:ty $(, $querytype:ty)?>
|
|
||||||
) => {
|
|
||||||
$crate::paste::paste! {
|
|
||||||
$crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name);
|
|
||||||
type $name = $crate::storage::types::StorageDoubleMap<
|
|
||||||
[<$name Instance>],
|
|
||||||
$hasher1,
|
|
||||||
$key1,
|
|
||||||
$hasher2,
|
|
||||||
$key2,
|
|
||||||
$value,
|
|
||||||
$( $querytype )?
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(
|
|
||||||
$pallet:ident,
|
|
||||||
$name:ident
|
|
||||||
=> NMap<Key<$(($hasher:ty, $key:ty)),+>, $value:ty $(, $querytype:ty)?>
|
|
||||||
) => {
|
|
||||||
$crate::paste::paste! {
|
|
||||||
$crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name);
|
|
||||||
type $name = $crate::storage::types::StorageNMap<
|
|
||||||
[<$name Instance>],
|
|
||||||
(
|
|
||||||
$( $crate::storage::types::Key<$hasher, $key>, )+
|
|
||||||
),
|
|
||||||
$value,
|
|
||||||
$( $querytype )?
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($pallet:ident, $name:ident => Value<$value:ty $(, $querytype:ty)?>) => {
|
|
||||||
$crate::paste::paste! {
|
|
||||||
$crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name);
|
|
||||||
type $name = $crate::storage::types::StorageValue<
|
|
||||||
[<$name Instance>],
|
|
||||||
$value,
|
|
||||||
$( $querytype )?
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// with generic for $name.
|
|
||||||
(
|
|
||||||
$pallet:ident,
|
|
||||||
$name:ident<$t:ident : $bounds:tt>
|
|
||||||
=> Map<($hasher:ty, $key:ty), $value:ty $(, $querytype:ty)?>
|
|
||||||
) => {
|
|
||||||
$crate::paste::paste! {
|
|
||||||
$crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name);
|
|
||||||
#[allow(type_alias_bounds)]
|
|
||||||
type $name<$t : $bounds> = $crate::storage::types::StorageMap<
|
|
||||||
[<$name Instance>],
|
|
||||||
$hasher,
|
|
||||||
$key,
|
|
||||||
$value,
|
|
||||||
$( $querytype )?
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(
|
|
||||||
$pallet:ident,
|
|
||||||
$name:ident<$t:ident : $bounds:tt>
|
|
||||||
=> DoubleMap<($hasher1:ty, $key1:ty), ($hasher2:ty, $key2:ty), $value:ty $(, $querytype:ty)?>
|
|
||||||
) => {
|
|
||||||
$crate::paste::paste! {
|
|
||||||
$crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name);
|
|
||||||
#[allow(type_alias_bounds)]
|
|
||||||
type $name<$t : $bounds> = $crate::storage::types::StorageDoubleMap<
|
|
||||||
[<$name Instance>],
|
|
||||||
$hasher1,
|
|
||||||
$key1,
|
|
||||||
$hasher2,
|
|
||||||
$key2,
|
|
||||||
$value,
|
|
||||||
$( $querytype )?
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(
|
|
||||||
$pallet:ident,
|
|
||||||
$name:ident<$t:ident : $bounds:tt>
|
|
||||||
=> NMap<$(($hasher:ty, $key:ty),)+ $value:ty $(, $querytype:ty)?>
|
|
||||||
) => {
|
|
||||||
$crate::paste::paste! {
|
|
||||||
$crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name);
|
|
||||||
#[allow(type_alias_bounds)]
|
|
||||||
type $name<$t : $bounds> = $crate::storage::types::StorageNMap<
|
|
||||||
[<$name Instance>],
|
|
||||||
(
|
|
||||||
$( $crate::storage::types::Key<$hasher, $key>, )+
|
|
||||||
),
|
|
||||||
$value,
|
|
||||||
$( $querytype )?
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($pallet:ident, $name:ident<$t:ident : $bounds:tt> => Value<$value:ty $(, $querytype:ty)?>) => {
|
|
||||||
$crate::paste::paste! {
|
|
||||||
$crate::generate_storage_alias!(@GENERATE_INSTANCE_STRUCT $pallet, $name);
|
|
||||||
#[allow(type_alias_bounds)]
|
|
||||||
type $name<$t : $bounds> = $crate::storage::types::StorageValue<
|
|
||||||
[<$name Instance>],
|
|
||||||
$value,
|
|
||||||
$( $querytype )?
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// helper used in all arms.
|
|
||||||
(@GENERATE_INSTANCE_STRUCT $pallet:ident, $name:ident) => {
|
|
||||||
$crate::paste::paste! {
|
|
||||||
struct [<$name Instance>];
|
|
||||||
impl $crate::traits::StorageInstance for [<$name Instance>] {
|
|
||||||
fn pallet_prefix() -> &'static str { stringify!($pallet) }
|
|
||||||
const STORAGE_PREFIX: &'static str = stringify!($name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create new implementations of the [`Get`](crate::traits::Get) trait.
|
/// Create new implementations of the [`Get`](crate::traits::Get) trait.
|
||||||
///
|
///
|
||||||
@@ -995,16 +891,28 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generate_storage_alias_works() {
|
fn storage_alias_works() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
generate_storage_alias!(
|
#[crate::storage_alias]
|
||||||
|
type GenericData2<T> = StorageMap<
|
||||||
Test,
|
Test,
|
||||||
GenericData2<T: Config> => Map<(Blake2_128Concat, T::BlockNumber), T::BlockNumber>
|
Blake2_128Concat,
|
||||||
);
|
<T as Config>::BlockNumber,
|
||||||
|
<T as Config>::BlockNumber,
|
||||||
|
>;
|
||||||
|
|
||||||
assert_eq!(Module::<Test>::generic_data2(5), None);
|
assert_eq!(Module::<Test>::generic_data2(5), None);
|
||||||
GenericData2::<Test>::insert(5, 5);
|
GenericData2::<Test>::insert(5, 5);
|
||||||
assert_eq!(Module::<Test>::generic_data2(5), Some(5));
|
assert_eq!(Module::<Test>::generic_data2(5), Some(5));
|
||||||
|
|
||||||
|
/// Some random docs that ensure that docs are accepted
|
||||||
|
#[crate::storage_alias]
|
||||||
|
pub type GenericData<T> = StorageMap<
|
||||||
|
Test2,
|
||||||
|
Blake2_128Concat,
|
||||||
|
<T as Config>::BlockNumber,
|
||||||
|
<T as Config>::BlockNumber,
|
||||||
|
>;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -348,12 +348,15 @@ pub mod test {
|
|||||||
use frame_support::traits::ConstU32;
|
use frame_support::traits::ConstU32;
|
||||||
use sp_io::TestExternalities;
|
use sp_io::TestExternalities;
|
||||||
|
|
||||||
crate::generate_storage_alias! { Prefix, Foo => Value<BoundedBTreeMap<u32, (), ConstU32<7>>> }
|
#[crate::storage_alias]
|
||||||
crate::generate_storage_alias! { Prefix, FooMap => Map<(Twox128, u32), BoundedBTreeMap<u32, (), ConstU32<7>>> }
|
type Foo = StorageValue<Prefix, BoundedBTreeMap<u32, (), ConstU32<7>>>;
|
||||||
crate::generate_storage_alias! {
|
|
||||||
Prefix,
|
#[crate::storage_alias]
|
||||||
FooDoubleMap => DoubleMap<(Twox128, u32), (Twox128, u32), BoundedBTreeMap<u32, (), ConstU32<7>>>
|
type FooMap = StorageMap<Prefix, Twox128, u32, BoundedBTreeMap<u32, (), ConstU32<7>>>;
|
||||||
}
|
|
||||||
|
#[crate::storage_alias]
|
||||||
|
type FooDoubleMap =
|
||||||
|
StorageDoubleMap<Prefix, Twox128, u32, Twox128, u32, BoundedBTreeMap<u32, (), ConstU32<7>>>;
|
||||||
|
|
||||||
fn map_from_keys<K>(keys: &[K]) -> BTreeMap<K, ()>
|
fn map_from_keys<K>(keys: &[K]) -> BTreeMap<K, ()>
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -322,12 +322,15 @@ pub mod test {
|
|||||||
use frame_support::traits::ConstU32;
|
use frame_support::traits::ConstU32;
|
||||||
use sp_io::TestExternalities;
|
use sp_io::TestExternalities;
|
||||||
|
|
||||||
crate::generate_storage_alias! { Prefix, Foo => Value<BoundedBTreeSet<u32, ConstU32<7>>> }
|
#[crate::storage_alias]
|
||||||
crate::generate_storage_alias! { Prefix, FooMap => Map<(Twox128, u32), BoundedBTreeSet<u32, ConstU32<7>>> }
|
type Foo = StorageValue<Prefix, BoundedBTreeSet<u32, ConstU32<7>>>;
|
||||||
crate::generate_storage_alias! {
|
|
||||||
Prefix,
|
#[crate::storage_alias]
|
||||||
FooDoubleMap => DoubleMap<(Twox128, u32), (Twox128, u32), BoundedBTreeSet<u32, ConstU32<7>>>
|
type FooMap = StorageMap<Prefix, Twox128, u32, BoundedBTreeSet<u32, ConstU32<7>>>;
|
||||||
}
|
|
||||||
|
#[crate::storage_alias]
|
||||||
|
type FooDoubleMap =
|
||||||
|
StorageDoubleMap<Prefix, Twox128, u32, Twox128, u32, BoundedBTreeSet<u32, ConstU32<7>>>;
|
||||||
|
|
||||||
fn set_from_keys<T>(keys: &[T]) -> BTreeSet<T>
|
fn set_from_keys<T>(keys: &[T]) -> BTreeSet<T>
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -672,12 +672,15 @@ pub mod test {
|
|||||||
use crate::{bounded_vec, traits::ConstU32, Twox128};
|
use crate::{bounded_vec, traits::ConstU32, Twox128};
|
||||||
use sp_io::TestExternalities;
|
use sp_io::TestExternalities;
|
||||||
|
|
||||||
crate::generate_storage_alias! { Prefix, Foo => Value<BoundedVec<u32, ConstU32<7>>> }
|
#[crate::storage_alias]
|
||||||
crate::generate_storage_alias! { Prefix, FooMap => Map<(Twox128, u32), BoundedVec<u32, ConstU32<7>>> }
|
type Foo = StorageValue<Prefix, BoundedVec<u32, ConstU32<7>>>;
|
||||||
crate::generate_storage_alias! {
|
|
||||||
Prefix,
|
#[crate::storage_alias]
|
||||||
FooDoubleMap => DoubleMap<(Twox128, u32), (Twox128, u32), BoundedVec<u32, ConstU32<7>>>
|
type FooMap = StorageMap<Prefix, Twox128, u32, BoundedVec<u32, ConstU32<7>>>;
|
||||||
}
|
|
||||||
|
#[crate::storage_alias]
|
||||||
|
type FooDoubleMap =
|
||||||
|
StorageDoubleMap<Prefix, Twox128, u32, Twox128, u32, BoundedVec<u32, ConstU32<7>>>;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn slide_works() {
|
fn slide_works() {
|
||||||
|
|||||||
@@ -525,10 +525,8 @@ mod test_iterators {
|
|||||||
fn double_map_iter_from() {
|
fn double_map_iter_from() {
|
||||||
sp_io::TestExternalities::default().execute_with(|| {
|
sp_io::TestExternalities::default().execute_with(|| {
|
||||||
use crate::hash::Identity;
|
use crate::hash::Identity;
|
||||||
crate::generate_storage_alias!(
|
#[crate::storage_alias]
|
||||||
MyModule,
|
type MyDoubleMap = StorageDoubleMap<MyModule, Identity, u64, Identity, u64, u64>;
|
||||||
MyDoubleMap => DoubleMap<(Identity, u64), (Identity, u64), u64>
|
|
||||||
);
|
|
||||||
|
|
||||||
MyDoubleMap::insert(1, 10, 100);
|
MyDoubleMap::insert(1, 10, 100);
|
||||||
MyDoubleMap::insert(1, 21, 201);
|
MyDoubleMap::insert(1, 21, 201);
|
||||||
|
|||||||
@@ -384,7 +384,8 @@ mod test_iterators {
|
|||||||
fn map_iter_from() {
|
fn map_iter_from() {
|
||||||
sp_io::TestExternalities::default().execute_with(|| {
|
sp_io::TestExternalities::default().execute_with(|| {
|
||||||
use crate::hash::Identity;
|
use crate::hash::Identity;
|
||||||
crate::generate_storage_alias!(MyModule, MyMap => Map<(Identity, u64), u64>);
|
#[crate::storage_alias]
|
||||||
|
type MyMap = StorageMap<MyModule, Identity, u64, u64>;
|
||||||
|
|
||||||
MyMap::insert(1, 10);
|
MyMap::insert(1, 10);
|
||||||
MyMap::insert(2, 20);
|
MyMap::insert(2, 20);
|
||||||
|
|||||||
@@ -475,10 +475,12 @@ mod test_iterators {
|
|||||||
fn n_map_iter_from() {
|
fn n_map_iter_from() {
|
||||||
sp_io::TestExternalities::default().execute_with(|| {
|
sp_io::TestExternalities::default().execute_with(|| {
|
||||||
use crate::{hash::Identity, storage::Key as NMapKey};
|
use crate::{hash::Identity, storage::Key as NMapKey};
|
||||||
crate::generate_storage_alias!(
|
#[crate::storage_alias]
|
||||||
|
type MyNMap = StorageNMap<
|
||||||
MyModule,
|
MyModule,
|
||||||
MyNMap => NMap<Key<(Identity, u64), (Identity, u64), (Identity, u64)>, u64>
|
(NMapKey<Identity, u64>, NMapKey<Identity, u64>, NMapKey<Identity, u64>),
|
||||||
);
|
u64,
|
||||||
|
>;
|
||||||
|
|
||||||
MyNMap::insert((1, 1, 1), 11);
|
MyNMap::insert((1, 1, 1), 11);
|
||||||
MyNMap::insert((1, 1, 2), 21);
|
MyNMap::insert((1, 1, 2), 21);
|
||||||
@@ -518,11 +520,15 @@ mod test_iterators {
|
|||||||
let key_hash = NMap::hashed_key_for((1, 2));
|
let key_hash = NMap::hashed_key_for((1, 2));
|
||||||
|
|
||||||
{
|
{
|
||||||
crate::generate_storage_alias!(Test, NMap => DoubleMap<
|
#[crate::storage_alias]
|
||||||
(crate::Blake2_128Concat, u16),
|
type NMap = StorageDoubleMap<
|
||||||
(crate::Twox64Concat, u32),
|
Test,
|
||||||
u64
|
crate::Blake2_128Concat,
|
||||||
>);
|
u16,
|
||||||
|
crate::Twox64Concat,
|
||||||
|
u32,
|
||||||
|
u64,
|
||||||
|
>;
|
||||||
|
|
||||||
let value = NMap::get(1, 2).unwrap();
|
let value = NMap::get(1, 2).unwrap();
|
||||||
assert_eq!(value, 50);
|
assert_eq!(value, 50);
|
||||||
|
|||||||
@@ -1545,10 +1545,8 @@ mod test {
|
|||||||
fn prefix_iterator_pagination_works() {
|
fn prefix_iterator_pagination_works() {
|
||||||
TestExternalities::default().execute_with(|| {
|
TestExternalities::default().execute_with(|| {
|
||||||
use crate::{hash::Identity, storage::generator::map::StorageMap};
|
use crate::{hash::Identity, storage::generator::map::StorageMap};
|
||||||
crate::generate_storage_alias! {
|
#[crate::storage_alias]
|
||||||
MyModule,
|
type MyStorageMap = StorageMap<MyModule, Identity, u64, u64>;
|
||||||
MyStorageMap => Map<(Identity, u64), u64>
|
|
||||||
}
|
|
||||||
|
|
||||||
MyStorageMap::insert(1, 10);
|
MyStorageMap::insert(1, 10);
|
||||||
MyStorageMap::insert(2, 20);
|
MyStorageMap::insert(2, 20);
|
||||||
@@ -1663,12 +1661,13 @@ mod test {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::generate_storage_alias! { Prefix, Foo => Value<WeakBoundedVec<u32, ConstU32<7>>> }
|
#[crate::storage_alias]
|
||||||
crate::generate_storage_alias! { Prefix, FooMap => Map<(Twox128, u32), BoundedVec<u32, ConstU32<7>>> }
|
type Foo = StorageValue<Prefix, WeakBoundedVec<u32, ConstU32<7>>>;
|
||||||
crate::generate_storage_alias! {
|
#[crate::storage_alias]
|
||||||
Prefix,
|
type FooMap = StorageMap<Prefix, Twox128, u32, BoundedVec<u32, ConstU32<7>>>;
|
||||||
FooDoubleMap => DoubleMap<(Twox128, u32), (Twox128, u32), BoundedVec<u32, ConstU32<7>>>
|
#[crate::storage_alias]
|
||||||
}
|
type FooDoubleMap =
|
||||||
|
StorageDoubleMap<Prefix, Twox128, u32, Twox128, u32, BoundedVec<u32, ConstU32<7>>>;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn try_append_works() {
|
fn try_append_works() {
|
||||||
|
|||||||
@@ -544,7 +544,7 @@ mod test {
|
|||||||
use crate::{
|
use crate::{
|
||||||
hash::{StorageHasher as _, *},
|
hash::{StorageHasher as _, *},
|
||||||
metadata::{StorageEntryModifier, StorageHasher},
|
metadata::{StorageEntryModifier, StorageHasher},
|
||||||
storage::types::{Key, ValueQuery},
|
storage::types::{Key, Key as NMapKey, ValueQuery},
|
||||||
};
|
};
|
||||||
use sp_io::{hashing::twox_128, TestExternalities};
|
use sp_io::{hashing::twox_128, TestExternalities};
|
||||||
|
|
||||||
@@ -589,10 +589,8 @@ mod test {
|
|||||||
assert_eq!(AValueQueryWithAnOnEmpty::get((3,)), 10);
|
assert_eq!(AValueQueryWithAnOnEmpty::get((3,)), 10);
|
||||||
|
|
||||||
{
|
{
|
||||||
crate::generate_storage_alias!(test, Foo => NMap<
|
#[crate::storage_alias]
|
||||||
Key<(Blake2_128Concat, u16)>,
|
type Foo = StorageNMap<test, (NMapKey<Blake2_128Concat, u16>), u32>;
|
||||||
u32
|
|
||||||
>);
|
|
||||||
|
|
||||||
assert_eq!(Foo::contains_key((3,)), true);
|
assert_eq!(Foo::contains_key((3,)), true);
|
||||||
assert_eq!(Foo::get((3,)), Some(10));
|
assert_eq!(Foo::get((3,)), Some(10));
|
||||||
|
|||||||
@@ -321,12 +321,13 @@ pub mod test {
|
|||||||
use frame_support::traits::ConstU32;
|
use frame_support::traits::ConstU32;
|
||||||
use sp_io::TestExternalities;
|
use sp_io::TestExternalities;
|
||||||
|
|
||||||
crate::generate_storage_alias! { Prefix, Foo => Value<WeakBoundedVec<u32, ConstU32<7>>> }
|
#[crate::storage_alias]
|
||||||
crate::generate_storage_alias! { Prefix, FooMap => Map<(Twox128, u32), WeakBoundedVec<u32, ConstU32<7>>> }
|
type Foo = StorageValue<Prefix, WeakBoundedVec<u32, ConstU32<7>>>;
|
||||||
crate::generate_storage_alias! {
|
#[crate::storage_alias]
|
||||||
Prefix,
|
type FooMap = StorageMap<Prefix, Twox128, u32, WeakBoundedVec<u32, ConstU32<7>>>;
|
||||||
FooDoubleMap => DoubleMap<(Twox128, u32), (Twox128, u32), WeakBoundedVec<u32, ConstU32<7>>>
|
#[crate::storage_alias]
|
||||||
}
|
type FooDoubleMap =
|
||||||
|
StorageDoubleMap<Prefix, Twox128, u32, Twox128, u32, WeakBoundedVec<u32, ConstU32<7>>>;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bound_returns_correct_value() {
|
fn bound_returns_correct_value() {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
dispatch::{Parameter, UnfilteredDispatchable},
|
dispatch::{Parameter, UnfilteredDispatchable},
|
||||||
|
pallet_prelude::ValueQuery,
|
||||||
storage::unhashed,
|
storage::unhashed,
|
||||||
traits::{
|
traits::{
|
||||||
ConstU32, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize,
|
ConstU32, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize,
|
||||||
@@ -1631,3 +1632,17 @@ fn assert_type_all_pallets_without_system_reversed_is_correct() {
|
|||||||
_a(t)
|
_a(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_storage_alias() {
|
||||||
|
#[frame_support::storage_alias]
|
||||||
|
type Value<T: pallet::Config>
|
||||||
|
where
|
||||||
|
<T as frame_system::Config>::AccountId: From<SomeType1> + SomeAssociation1,
|
||||||
|
= StorageValue<pallet::Pallet<T>, u32, ValueQuery>;
|
||||||
|
|
||||||
|
TestExternalities::default().execute_with(|| {
|
||||||
|
pallet::Value::<Runtime>::put(10);
|
||||||
|
assert_eq!(10, Value::<Runtime>::get());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
dispatch::UnfilteredDispatchable,
|
dispatch::UnfilteredDispatchable,
|
||||||
|
pallet_prelude::ValueQuery,
|
||||||
storage::unhashed,
|
storage::unhashed,
|
||||||
traits::{ConstU32, GetCallName, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade},
|
traits::{ConstU32, GetCallName, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade},
|
||||||
weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays},
|
weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays},
|
||||||
@@ -821,3 +822,15 @@ fn test_pallet_info_access() {
|
|||||||
assert_eq!(<Example2 as frame_support::traits::PalletInfoAccess>::index(), 3);
|
assert_eq!(<Example2 as frame_support::traits::PalletInfoAccess>::index(), 3);
|
||||||
assert_eq!(<Instance1Example2 as frame_support::traits::PalletInfoAccess>::index(), 4);
|
assert_eq!(<Instance1Example2 as frame_support::traits::PalletInfoAccess>::index(), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_storage_alias() {
|
||||||
|
#[frame_support::storage_alias]
|
||||||
|
type Value<T: pallet::Config<I>, I: 'static> =
|
||||||
|
StorageValue<pallet::Pallet<T, I>, u32, ValueQuery>;
|
||||||
|
|
||||||
|
TestExternalities::default().execute_with(|| {
|
||||||
|
pallet::Value::<Runtime, pallet::Instance1>::put(10);
|
||||||
|
assert_eq!(10, Value::<Runtime, pallet::Instance1>::get());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
//! Migrate the reference counting state.
|
//! Migrate the reference counting state.
|
||||||
|
|
||||||
|
use crate::{Config, Pallet};
|
||||||
use codec::{Decode, Encode, FullCodec};
|
use codec::{Decode, Encode, FullCodec};
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
pallet_prelude::ValueQuery, traits::PalletInfoAccess, weights::Weight, Blake2_128Concat,
|
pallet_prelude::ValueQuery, traits::PalletInfoAccess, weights::Weight, Blake2_128Concat,
|
||||||
@@ -52,43 +53,24 @@ pub trait V2ToV3 {
|
|||||||
type AccountData: 'static + FullCodec;
|
type AccountData: 'static + FullCodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ### Warning
|
#[frame_support::storage_alias]
|
||||||
//
|
type UpgradedToU32RefCount<T: Config> = StorageValue<Pallet<T>, bool, ValueQuery>;
|
||||||
// The call below is only valid because the name System is enforced
|
|
||||||
// at runtime construction level for the system pallet.
|
|
||||||
frame_support::generate_storage_alias!(
|
|
||||||
System, UpgradedToU32RefCount => Value<
|
|
||||||
bool,
|
|
||||||
ValueQuery
|
|
||||||
>
|
|
||||||
);
|
|
||||||
|
|
||||||
// ### Warning
|
#[frame_support::storage_alias]
|
||||||
//
|
type UpgradedToTripleRefCount<T: Config> = StorageValue<Pallet<T>, bool, ValueQuery>;
|
||||||
// The call below is only valid because the name System is enforced
|
|
||||||
// at runtime construction level for the system pallet.
|
|
||||||
frame_support::generate_storage_alias!(
|
|
||||||
System, UpgradedToTripleRefCount => Value<
|
|
||||||
bool,
|
|
||||||
ValueQuery
|
|
||||||
>
|
|
||||||
);
|
|
||||||
|
|
||||||
// ### Warning
|
#[frame_support::storage_alias]
|
||||||
//
|
type Account<V, T: Config> = StorageMap<
|
||||||
// The call below is only valid because the name System is enforced
|
Pallet<T>,
|
||||||
// at runtime construction level for the system pallet.
|
Blake2_128Concat,
|
||||||
frame_support::generate_storage_alias!(
|
<V as V2ToV3>::AccountId,
|
||||||
System, Account<T: V2ToV3> => Map<
|
AccountInfo<<V as V2ToV3>::Index, <V as V2ToV3>::AccountData>,
|
||||||
(Blake2_128Concat, T::AccountId),
|
>;
|
||||||
AccountInfo<T::Index, T::AccountData>
|
|
||||||
>
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Migrate from unique `u8` reference counting to triple `u32` reference counting.
|
/// Migrate from unique `u8` reference counting to triple `u32` reference counting.
|
||||||
pub fn migrate_from_single_u8_to_triple_ref_count<T: V2ToV3>() -> Weight {
|
pub fn migrate_from_single_u8_to_triple_ref_count<V: V2ToV3, T: Config>() -> Weight {
|
||||||
let mut translated: usize = 0;
|
let mut translated: usize = 0;
|
||||||
<Account<T>>::translate::<(T::Index, u8, T::AccountData), _>(|_key, (nonce, rc, data)| {
|
<Account<V, T>>::translate::<(V::Index, u8, V::AccountData), _>(|_key, (nonce, rc, data)| {
|
||||||
translated += 1;
|
translated += 1;
|
||||||
Some(AccountInfo { nonce, consumers: rc as RefCount, providers: 1, sufficients: 0, data })
|
Some(AccountInfo { nonce, consumers: rc as RefCount, providers: 1, sufficients: 0, data })
|
||||||
});
|
});
|
||||||
@@ -97,15 +79,15 @@ pub fn migrate_from_single_u8_to_triple_ref_count<T: V2ToV3>() -> Weight {
|
|||||||
"Applied migration from single u8 to triple reference counting to {:?} elements.",
|
"Applied migration from single u8 to triple reference counting to {:?} elements.",
|
||||||
translated
|
translated
|
||||||
);
|
);
|
||||||
<UpgradedToU32RefCount>::put(true);
|
<UpgradedToU32RefCount<T>>::put(true);
|
||||||
<UpgradedToTripleRefCount>::put(true);
|
<UpgradedToTripleRefCount<T>>::put(true);
|
||||||
Weight::max_value()
|
Weight::max_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Migrate from unique `u32` reference counting to triple `u32` reference counting.
|
/// Migrate from unique `u32` reference counting to triple `u32` reference counting.
|
||||||
pub fn migrate_from_single_to_triple_ref_count<T: V2ToV3>() -> Weight {
|
pub fn migrate_from_single_to_triple_ref_count<V: V2ToV3, T: Config>() -> Weight {
|
||||||
let mut translated: usize = 0;
|
let mut translated: usize = 0;
|
||||||
<Account<T>>::translate::<(T::Index, RefCount, T::AccountData), _>(
|
<Account<V, T>>::translate::<(V::Index, RefCount, V::AccountData), _>(
|
||||||
|_key, (nonce, consumers, data)| {
|
|_key, (nonce, consumers, data)| {
|
||||||
translated += 1;
|
translated += 1;
|
||||||
Some(AccountInfo { nonce, consumers, providers: 1, sufficients: 0, data })
|
Some(AccountInfo { nonce, consumers, providers: 1, sufficients: 0, data })
|
||||||
@@ -116,14 +98,14 @@ pub fn migrate_from_single_to_triple_ref_count<T: V2ToV3>() -> Weight {
|
|||||||
"Applied migration from single to triple reference counting to {:?} elements.",
|
"Applied migration from single to triple reference counting to {:?} elements.",
|
||||||
translated
|
translated
|
||||||
);
|
);
|
||||||
<UpgradedToTripleRefCount>::put(true);
|
<UpgradedToTripleRefCount<T>>::put(true);
|
||||||
Weight::max_value()
|
Weight::max_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Migrate from dual `u32` reference counting to triple `u32` reference counting.
|
/// Migrate from dual `u32` reference counting to triple `u32` reference counting.
|
||||||
pub fn migrate_from_dual_to_triple_ref_count<T: V2ToV3>() -> Weight {
|
pub fn migrate_from_dual_to_triple_ref_count<V: V2ToV3, T: Config>() -> Weight {
|
||||||
let mut translated: usize = 0;
|
let mut translated: usize = 0;
|
||||||
<Account<T>>::translate::<(T::Index, RefCount, RefCount, T::AccountData), _>(
|
<Account<V, T>>::translate::<(V::Index, RefCount, RefCount, V::AccountData), _>(
|
||||||
|_key, (nonce, consumers, providers, data)| {
|
|_key, (nonce, consumers, providers, data)| {
|
||||||
translated += 1;
|
translated += 1;
|
||||||
Some(AccountInfo { nonce, consumers, providers, sufficients: 0, data })
|
Some(AccountInfo { nonce, consumers, providers, sufficients: 0, data })
|
||||||
@@ -134,6 +116,6 @@ pub fn migrate_from_dual_to_triple_ref_count<T: V2ToV3>() -> Weight {
|
|||||||
"Applied migration from dual to triple reference counting to {:?} elements.",
|
"Applied migration from dual to triple reference counting to {:?} elements.",
|
||||||
translated
|
translated
|
||||||
);
|
);
|
||||||
<UpgradedToTripleRefCount>::put(true);
|
<UpgradedToTripleRefCount<T>>::put(true);
|
||||||
Weight::max_value()
|
Weight::max_value()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user