mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 23:31:07 +00:00
Refactor away from opaque hashes (#5226)
* System.BlockHash * Fix hash * Introduce K/V iteration in all _concat maps Also move across: - System.Account (blake2_128_concat) - Balances.Locks (twox_64_concat) - ElectionsPhragmen.VotesOf (twox_64_concat) - ElectionsPhragmen.StakeOf (twox_64_concat) - Identity.IdentityOf (twox_64_concat) - Identity.SubsOf (twox_64_concat) - Society.Payouts (twox_64_concat) - Session.NextKeys (twox_64_concat) - Identity.SuperOf (blake2_128_concat) - Session.KeyOwner (blake2_128_concat) - Society.SuspendedCandidates (twox_64_concat) - Society.SuspendedMembers (twox_64_concat) - Society.Vouching (twox_64_concat) - Society.Strikes (twox_64_concat) - System.EventTopics - Balances.Account * Build fixes * Ensure migration happens in correct order * Staking.* * Vesting.* Offences.* * Democracy.* * Babe.* Collective.* * Grandpa.* * Assets.* Benchmark.* Contracts.* Elections.* Asset.* Nicks.* Also introduce real account list * ImOnline.* * Treasury.* * Recovery.* * Final bits. * Docs * Fix one test * Fix test * All passing except the UI tests * Remove linked_map part 1 * Remove linked_map * Some iterator utils for double maps. * Remove old migrations * Introduce tombstone for LinkedMap type * Migration for genesis hash * Fix build * Fix hash * Rename Map is_linked -> unused, keeping backwards compat (#5256) * Update frame/balances/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/elections/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Remove old migration code. * Update frame/system/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Update bin/node/runtime/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Fix hash * fix session migration * Fix watning Co-authored-by: Jaco Greeff <jacogr@gmail.com> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: Robert Habermeier <rphmeier@gmail.com>
This commit is contained in:
@@ -40,7 +40,7 @@ impl system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
|
||||
@@ -162,7 +162,7 @@ impl system::Trait for Runtime {
|
||||
/// This type is being generated by `construct_runtime!`.
|
||||
type ModuleToIndex = ModuleToIndex;
|
||||
/// What to do if a new account is created.
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
/// What to do if an account is fully reaped from the system.
|
||||
type OnKilledAccount = ();
|
||||
/// The data to be stored in an account.
|
||||
|
||||
@@ -140,6 +140,7 @@ impl frame_system::Trait for Runtime {
|
||||
type Version = Version;
|
||||
type ModuleToIndex = ModuleToIndex;
|
||||
type AccountData = pallet_balances::AccountData<Balance>;
|
||||
type MigrateAccount = (Balances, Identity, Democracy, Elections, ImOnline, Recovery, Session, Society, Staking, Vesting);
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
@@ -228,11 +228,11 @@ decl_error! {
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Assets {
|
||||
/// The number of units of assets held by any given account.
|
||||
Balances: map hasher(blake2_256) (T::AssetId, T::AccountId) => T::Balance;
|
||||
Balances: map hasher(blake2_128_concat) (T::AssetId, T::AccountId) => T::Balance;
|
||||
/// The next asset identifier up for grabs.
|
||||
NextAssetId get(fn next_asset_id): T::AssetId;
|
||||
/// The total unit supply of an asset.
|
||||
TotalSupply: map hasher(blake2_256) T::AssetId => T::Balance;
|
||||
TotalSupply: map hasher(twox_64_concat) T::AssetId => T::Balance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
|
||||
@@ -62,7 +62,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -432,7 +432,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ decl_storage! {
|
||||
/// We reset all segments and return to `0` at the beginning of every
|
||||
/// epoch.
|
||||
SegmentIndex build(|_| 0): u32;
|
||||
UnderConstruction: map hasher(blake2_256) u32 => Vec<[u8; 32 /* VRF_OUTPUT_LENGTH */]>;
|
||||
UnderConstruction: map hasher(twox_64_concat) u32 => Vec<[u8; 32 /* VRF_OUTPUT_LENGTH */]>;
|
||||
|
||||
/// Temporary value (cleared at block finalization) which is `Some`
|
||||
/// if per-block initialization has already been called for current block.
|
||||
@@ -188,6 +188,12 @@ decl_module! {
|
||||
Self::deposit_vrf_output(&vrf_output);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
for i in 0..=SegmentIndex::get() {
|
||||
UnderConstruction::migrate_key_from_blake(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ impl frame_system::Trait for Test {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ use frame_support::{
|
||||
Currency, OnKilledAccount, OnUnbalanced, TryDrop, StoredMap,
|
||||
WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement,
|
||||
Imbalance, SignedImbalance, ReservableCurrency, Get, ExistenceRequirement::KeepAlive,
|
||||
ExistenceRequirement::AllowDeath, IsDeadAccount, BalanceStatus as Status
|
||||
ExistenceRequirement::AllowDeath, IsDeadAccount, BalanceStatus as Status, MigrateAccount,
|
||||
}
|
||||
};
|
||||
use sp_runtime::{
|
||||
@@ -372,11 +372,11 @@ decl_storage! {
|
||||
/// is ever zero, then the entry *MUST* be removed.
|
||||
///
|
||||
/// NOTE: This is only used in the case that this module is used to store balances.
|
||||
pub Account: map hasher(blake2_256) T::AccountId => AccountData<T::Balance>;
|
||||
pub Account: map hasher(blake2_128_concat) T::AccountId => AccountData<T::Balance>;
|
||||
|
||||
/// Any liquidity locks on some account balances.
|
||||
/// NOTE: Should only be accessed when setting, changing and freeing a lock.
|
||||
pub Locks get(fn locks): map hasher(blake2_256) T::AccountId => Vec<BalanceLock<T::Balance>>;
|
||||
pub Locks get(fn locks): map hasher(blake2_128_concat) T::AccountId => Vec<BalanceLock<T::Balance>>;
|
||||
|
||||
/// Storage version of the pallet.
|
||||
///
|
||||
@@ -531,6 +531,12 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance> MigrateAccount<T::AccountId> for Module<T, I> {
|
||||
fn migrate_account(account: &T::AccountId) {
|
||||
Locks::<T, I>::migrate_key_from_blake(account);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance> Module<T, I> {
|
||||
// PRIVATE MUTABLES
|
||||
|
||||
@@ -850,7 +856,7 @@ impl<T: Subtrait<I>, I: Instance> frame_system::Trait for ElevatedTrait<T, I> {
|
||||
type AvailableBlockRatio = T::AvailableBlockRatio;
|
||||
type Version = T::Version;
|
||||
type ModuleToIndex = T::ModuleToIndex;
|
||||
type OnNewAccount = T::OnNewAccount;
|
||||
type MigrateAccount = (); type OnNewAccount = T::OnNewAccount;
|
||||
type OnKilledAccount = T::OnKilledAccount;
|
||||
type AccountData = T::AccountData;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = super::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -66,7 +66,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = super::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = Module<Test>;
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -41,10 +41,10 @@ pub trait Trait: system::Trait {
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Benchmark {
|
||||
MyMemberList: Vec<T::AccountId>;
|
||||
MyMemberMap: map hasher(blake2_256) T::AccountId => bool;
|
||||
MyMemberMap: map hasher(blake2_128_concat) T::AccountId => bool;
|
||||
MyValue: u32;
|
||||
MyMap: map hasher(blake2_256) u32 => u32;
|
||||
MyDoubleMap: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => u32;
|
||||
MyMap: map hasher(twox_64_concat) u32 => u32;
|
||||
MyDoubleMap: double_map hasher(twox_64_concat) u32, hasher(identity) u32 => u32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -106,10 +106,10 @@ decl_storage! {
|
||||
pub Proposals get(fn proposals): Vec<T::Hash>;
|
||||
/// Actual proposal for a given hash, if it's current.
|
||||
pub ProposalOf get(fn proposal_of):
|
||||
map hasher(blake2_256) T::Hash => Option<<T as Trait<I>>::Proposal>;
|
||||
map hasher(identity) T::Hash => Option<<T as Trait<I>>::Proposal>;
|
||||
/// Votes on a given proposal, if it is ongoing.
|
||||
pub Voting get(fn voting):
|
||||
map hasher(blake2_256) T::Hash => Option<Votes<T::AccountId, T::BlockNumber>>;
|
||||
map hasher(identity) T::Hash => Option<Votes<T::AccountId, T::BlockNumber>>;
|
||||
/// Proposals so far.
|
||||
pub ProposalCount get(fn proposal_count): u32;
|
||||
/// The current members of the collective. This is stored sorted (just by value).
|
||||
@@ -168,6 +168,17 @@ decl_error! {
|
||||
}
|
||||
}
|
||||
|
||||
mod migration {
|
||||
use super::*;
|
||||
|
||||
pub fn migrate<T: Trait<I>, I: Instance>() {
|
||||
for p in Proposals::<T, I>::get().into_iter() {
|
||||
ProposalOf::<T, I>::migrate_key_from_blake(&p);
|
||||
Voting::<T, I>::migrate_key_from_blake(&p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this module is not benchmarked. The weights are obtained based on the similarity of the
|
||||
// executed logic with other democracy function. Note that councillor operations are assigned to the
|
||||
// operational class.
|
||||
@@ -177,6 +188,10 @@ decl_module! {
|
||||
|
||||
fn deposit_event() = default;
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
migration::migrate::<T, I>();
|
||||
}
|
||||
|
||||
/// Set the collective's membership.
|
||||
///
|
||||
/// - `new_members`: The new member list. Be nice to the chain and
|
||||
@@ -535,7 +550,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
impl Trait<Instance1> for Test {
|
||||
|
||||
@@ -98,7 +98,6 @@ mod rent;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod migration;
|
||||
|
||||
use crate::exec::ExecutionContext;
|
||||
use crate::account_db::{AccountDb, DirectAccountDb};
|
||||
@@ -667,10 +666,6 @@ decl_module! {
|
||||
fn on_finalize() {
|
||||
GasSpent::kill();
|
||||
}
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
migration::on_runtime_upgrade::<T>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -934,13 +929,13 @@ decl_storage! {
|
||||
/// Current cost schedule for contracts.
|
||||
CurrentSchedule get(fn current_schedule) config(): Schedule = Schedule::default();
|
||||
/// A mapping from an original code hash to the original code, untouched by instrumentation.
|
||||
pub PristineCode: map hasher(blake2_256) CodeHash<T> => Option<Vec<u8>>;
|
||||
pub PristineCode: map hasher(identity) CodeHash<T> => Option<Vec<u8>>;
|
||||
/// A mapping between an original code hash and instrumented wasm code, ready for execution.
|
||||
pub CodeStorage: map hasher(blake2_256) CodeHash<T> => Option<wasm::PrefabWasmModule>;
|
||||
pub CodeStorage: map hasher(identity) CodeHash<T> => Option<wasm::PrefabWasmModule>;
|
||||
/// The subtrie counter.
|
||||
pub AccountCounter: u64 = 0;
|
||||
/// The code associated with a given account.
|
||||
pub ContractInfoOf: map hasher(blake2_256) T::AccountId => Option<ContractInfo<T>>;
|
||||
pub ContractInfoOf: map hasher(twox_64_concat) T::AccountId => Option<ContractInfo<T>>;
|
||||
/// The price of one unit of gas.
|
||||
GasPrice get(fn gas_price) config(): BalanceOf<T> = 1.into();
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Migration code to update storage.
|
||||
|
||||
use super::*;
|
||||
use frame_support::storage::migration::{put_storage_value, take_storage_value, StorageIterator};
|
||||
|
||||
pub fn on_runtime_upgrade<T: Trait>() {
|
||||
change_name_contract_to_contracts::<T>()
|
||||
}
|
||||
|
||||
// Change the storage name used by this pallet from `Contract` to `Contracts`.
|
||||
//
|
||||
// Since the format of the storage items themselves have not changed, we do not
|
||||
// need to keep track of a storage version. If the runtime does not need to be
|
||||
// upgraded, nothing here will happen anyway.
|
||||
|
||||
fn change_name_contract_to_contracts<T: Trait>() {
|
||||
sp_runtime::print("Migrating Contracts.");
|
||||
|
||||
if let Some(gas_spent) = take_storage_value::<Gas>(b"Contract", b"GasSpent", &[]) {
|
||||
put_storage_value(b"Contracts", b"GasSpent", &[], gas_spent);
|
||||
}
|
||||
|
||||
if let Some(current_schedule) = take_storage_value::<Schedule>(b"Contract", b"CurrentSchedule", &[]) {
|
||||
put_storage_value(b"Contracts", b"CurrentSchedule", &[], current_schedule);
|
||||
}
|
||||
|
||||
for (hash, pristine_code) in StorageIterator::<Vec<u8>>::new(b"Contract", b"PristineCode").drain() {
|
||||
put_storage_value(b"Contracts", b"PristineCode", &hash, pristine_code);
|
||||
}
|
||||
|
||||
for (hash, code_storage) in StorageIterator::<wasm::PrefabWasmModule>::new(b"Contract", b"CodeStorage").drain() {
|
||||
put_storage_value(b"Contracts", b"CodeStorage", &hash, code_storage);
|
||||
}
|
||||
|
||||
if let Some(current_schedule) = take_storage_value::<u64>(b"Contract", b"AccountCounter", &[]) {
|
||||
put_storage_value(b"Contracts", b"AccountCounter", &[], current_schedule);
|
||||
}
|
||||
|
||||
for (hash, contract_info_of) in StorageIterator::<ContractInfo<T>>::new(b"Contract", b"ContractInfoOf").drain() {
|
||||
put_storage_value(b"Contracts", b"ContractInfoOf", &hash, contract_info_of);
|
||||
}
|
||||
|
||||
if let Some(get_price) = take_storage_value::<BalanceOf<T>>(b"Contract", b"GetPrice", &[]) {
|
||||
put_storage_value(b"Contracts", b"GetPrice", &[], get_price);
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = Contracts;
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
|
||||
@@ -159,7 +159,7 @@ use sp_runtime::{
|
||||
};
|
||||
use codec::{Ref, Encode, Decode, Input, Output};
|
||||
use frame_support::{
|
||||
decl_module, decl_storage, decl_event, decl_error, ensure, Parameter,
|
||||
decl_module, decl_storage, decl_event, decl_error, ensure, Parameter, IterableStorageMap,
|
||||
weights::SimpleDispatchInfo,
|
||||
traits::{
|
||||
Currency, ReservableCurrency, LockableCurrency, WithdrawReason, LockIdentifier, Get,
|
||||
@@ -170,6 +170,7 @@ use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
|
||||
mod vote_threshold;
|
||||
pub use vote_threshold::{Approved, VoteThreshold};
|
||||
use frame_support::traits::MigrateAccount;
|
||||
|
||||
const DEMOCRACY_ID: LockIdentifier = *b"democrac";
|
||||
|
||||
@@ -420,11 +421,11 @@ decl_storage! {
|
||||
/// Map of hashes to the proposal preimage, along with who registered it and their deposit.
|
||||
/// The block number is the block at which it was deposited.
|
||||
pub Preimages:
|
||||
map hasher(blake2_256) T::Hash
|
||||
map hasher(identity) T::Hash
|
||||
=> Option<(Vec<u8>, T::AccountId, BalanceOf<T>, T::BlockNumber)>;
|
||||
/// Those who have locked a deposit.
|
||||
pub DepositOf get(fn deposit_of):
|
||||
map hasher(blake2_256) PropIndex => Option<(BalanceOf<T>, Vec<T::AccountId>)>;
|
||||
map hasher(twox_64_concat) PropIndex => Option<(BalanceOf<T>, Vec<T::AccountId>)>;
|
||||
|
||||
/// The next free referendum index, aka the number of referenda started so far.
|
||||
pub ReferendumCount get(fn referendum_count) build(|_| 0 as ReferendumIndex): ReferendumIndex;
|
||||
@@ -433,32 +434,32 @@ decl_storage! {
|
||||
pub LowestUnbaked get(fn lowest_unbaked) build(|_| 0 as ReferendumIndex): ReferendumIndex;
|
||||
/// Information concerning any given referendum.
|
||||
pub ReferendumInfoOf get(fn referendum_info):
|
||||
map hasher(blake2_256) ReferendumIndex
|
||||
map hasher(twox_64_concat) ReferendumIndex
|
||||
=> Option<ReferendumInfo<T::BlockNumber, T::Hash>>;
|
||||
/// Queue of successful referenda to be dispatched. Stored ordered by block number.
|
||||
pub DispatchQueue get(fn dispatch_queue): Vec<(T::BlockNumber, T::Hash, ReferendumIndex)>;
|
||||
|
||||
/// Get the voters for the current proposal.
|
||||
pub VotersFor get(fn voters_for):
|
||||
map hasher(blake2_256) ReferendumIndex => Vec<T::AccountId>;
|
||||
map hasher(twox_64_concat) ReferendumIndex => Vec<T::AccountId>;
|
||||
|
||||
/// Get the vote in a given referendum of a particular voter. The result is meaningful only
|
||||
/// if `voters_for` includes the voter when called with the referendum (you'll get the
|
||||
/// default `Vote` value otherwise). If you don't want to check `voters_for`, then you can
|
||||
/// also check for simple existence with `VoteOf::contains_key` first.
|
||||
pub VoteOf get(fn vote_of): map hasher(blake2_256) (ReferendumIndex, T::AccountId) => Vote;
|
||||
pub VoteOf get(fn vote_of): map hasher(twox_64_concat) (ReferendumIndex, T::AccountId) => Vote;
|
||||
|
||||
/// Who is able to vote for whom. Value is the fund-holding account, key is the
|
||||
/// vote-transaction-sending account.
|
||||
pub Proxy get(fn proxy): map hasher(blake2_256) T::AccountId => Option<ProxyState<T::AccountId>>;
|
||||
pub Proxy get(fn proxy): map hasher(twox_64_concat) T::AccountId => Option<ProxyState<T::AccountId>>;
|
||||
|
||||
/// Get the account (and lock periods) to which another account is delegating vote.
|
||||
pub Delegations get(fn delegations):
|
||||
linked_map hasher(blake2_256) T::AccountId => (T::AccountId, Conviction);
|
||||
map hasher(twox_64_concat) T::AccountId => (T::AccountId, Conviction);
|
||||
|
||||
/// Accounts for which there are locks in action which may be removed at some point in the
|
||||
/// future. The value is the block number at which the lock expires and may be removed.
|
||||
pub Locks get(locks): map hasher(blake2_256) T::AccountId => Option<T::BlockNumber>;
|
||||
pub Locks get(locks): map hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
|
||||
|
||||
/// True if the last referendum tabled was submitted externally. False if it was a public
|
||||
/// proposal.
|
||||
@@ -473,10 +474,10 @@ decl_storage! {
|
||||
/// A record of who vetoed what. Maps proposal hash to a possible existent block number
|
||||
/// (until when it may not be resubmitted) and who vetoed it.
|
||||
pub Blacklist get(fn blacklist):
|
||||
map hasher(blake2_256) T::Hash => Option<(T::BlockNumber, Vec<T::AccountId>)>;
|
||||
map hasher(identity) T::Hash => Option<(T::BlockNumber, Vec<T::AccountId>)>;
|
||||
|
||||
/// Record of all proposals that have been subject to emergency cancellation.
|
||||
pub Cancellations: map hasher(blake2_256) T::Hash => bool;
|
||||
pub Cancellations: map hasher(identity) T::Hash => bool;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,9 +584,41 @@ decl_error! {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> MigrateAccount<T::AccountId> for Module<T> {
|
||||
fn migrate_account(a: &T::AccountId) {
|
||||
Proxy::<T>::migrate_key_from_blake(a);
|
||||
Locks::<T>::migrate_key_from_blake(a);
|
||||
Delegations::<T>::migrate_key_from_blake(a);
|
||||
for i in LowestUnbaked::get()..ReferendumCount::get() {
|
||||
VoteOf::<T>::migrate_key_from_blake((i, a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod migration {
|
||||
use super::*;
|
||||
pub fn migrate<T: Trait>() {
|
||||
Blacklist::<T>::remove_all();
|
||||
Cancellations::<T>::remove_all();
|
||||
for i in LowestUnbaked::get()..ReferendumCount::get() {
|
||||
VotersFor::<T>::migrate_key_from_blake(i);
|
||||
ReferendumInfoOf::<T>::migrate_key_from_blake(i);
|
||||
}
|
||||
for (p, h, _) in PublicProps::<T>::get().into_iter() {
|
||||
DepositOf::<T>::migrate_key_from_blake(p);
|
||||
Preimages::<T>::migrate_key_from_blake(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
type Error = Error<T>;
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
migration::migrate::<T>();
|
||||
}
|
||||
|
||||
/// The minimum period of locking and the period between a proposal being approved and enacted.
|
||||
///
|
||||
/// It should generally be a little more than the unstake period to ensure that
|
||||
@@ -1290,7 +1323,7 @@ impl<T: Trait> Module<T> {
|
||||
recursion_limit: u32,
|
||||
) -> (BalanceOf<T>, BalanceOf<T>) {
|
||||
if recursion_limit == 0 { return (Zero::zero(), Zero::zero()); }
|
||||
<Delegations<T>>::enumerate()
|
||||
<Delegations<T>>::iter()
|
||||
.filter(|(delegator, (delegate, _))|
|
||||
*delegate == to && !<VoteOf<T>>::contains_key(&(ref_index, delegator.clone()))
|
||||
).fold(
|
||||
@@ -1606,7 +1639,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -84,18 +84,18 @@
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use sp_runtime::{
|
||||
print, DispatchResult, DispatchError, Perbill,
|
||||
traits::{Zero, StaticLookup, Convert},
|
||||
print, DispatchResult, DispatchError, Perbill, traits::{Zero, StaticLookup, Convert},
|
||||
};
|
||||
use frame_support::{
|
||||
decl_storage, decl_event, ensure, decl_module, decl_error, weights::SimpleDispatchInfo,
|
||||
traits::{
|
||||
storage::{StorageMap, IterableStorageMap}, traits::{
|
||||
Currency, Get, LockableCurrency, LockIdentifier, ReservableCurrency, WithdrawReasons,
|
||||
ChangeMembers, OnUnbalanced, WithdrawReason, Contains, BalanceStatus
|
||||
}
|
||||
};
|
||||
use sp_phragmen::ExtendedBalance;
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
use frame_support::traits::MigrateAccount;
|
||||
|
||||
const MODULE_ID: LockIdentifier = *b"phrelect";
|
||||
|
||||
@@ -160,9 +160,9 @@ decl_storage! {
|
||||
pub ElectionRounds get(fn election_rounds): u32 = Zero::zero();
|
||||
|
||||
/// Votes of a particular voter, with the round index of the votes.
|
||||
pub VotesOf get(fn votes_of): linked_map hasher(blake2_256) T::AccountId => Vec<T::AccountId>;
|
||||
pub VotesOf get(fn votes_of): map hasher(twox_64_concat) T::AccountId => Vec<T::AccountId>;
|
||||
/// Locked stake of a voter.
|
||||
pub StakeOf get(fn stake_of): map hasher(blake2_256) T::AccountId => BalanceOf<T>;
|
||||
pub StakeOf get(fn stake_of): map hasher(twox_64_concat) T::AccountId => BalanceOf<T>;
|
||||
|
||||
/// The present candidate list. Sorted based on account-id. A current member or a runner can
|
||||
/// never enter this vector and is always implicitly assumed to be a candidate.
|
||||
@@ -474,6 +474,14 @@ decl_event!(
|
||||
}
|
||||
);
|
||||
|
||||
impl<T: Trait> MigrateAccount<T::AccountId> for Module<T> {
|
||||
fn migrate_account(a: &T::AccountId) {
|
||||
if StakeOf::<T>::migrate_key_from_blake(a).is_some() {
|
||||
VotesOf::<T>::migrate_key_from_blake(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Attempts to remove a member `who`. If a runner up exists, it is used as the replacement.
|
||||
/// Otherwise, `Ok(false)` is returned to signal the caller.
|
||||
@@ -637,7 +645,7 @@ impl<T: Trait> Module<T> {
|
||||
// previous runners_up are also always candidates for the next round.
|
||||
candidates.append(&mut Self::runners_up_ids());
|
||||
|
||||
let voters_and_votes = <VotesOf<T>>::enumerate()
|
||||
let voters_and_votes = VotesOf::<T>::iter()
|
||||
.map(|(v, i)| (v, i))
|
||||
.collect::<Vec<(T::AccountId, Vec<T::AccountId>)>>();
|
||||
let maybe_phragmen_result = sp_phragmen::elect::<_, _, _, T::CurrencyToVote, Perbill>(
|
||||
@@ -817,7 +825,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
@@ -1001,7 +1009,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn all_voters() -> Vec<u64> {
|
||||
<VotesOf<Test>>::enumerate().map(|(v, _)| v).collect::<Vec<u64>>()
|
||||
<VotesOf<Test>>::iter().map(|(v, _)| v).collect::<Vec<u64>>()
|
||||
}
|
||||
|
||||
fn balances(who: &u64) -> (u64, u64) {
|
||||
|
||||
@@ -236,16 +236,16 @@ decl_storage! {
|
||||
// [`all_approvals_of`]. Furthermore, each vector of scalars is chunked with the cap of
|
||||
// `APPROVAL_SET_SIZE`.
|
||||
pub ApprovalsOf get(fn approvals_of):
|
||||
map hasher(blake2_256) (T::AccountId, SetIndex) => Vec<ApprovalFlag>;
|
||||
map hasher(twox_64_concat) (T::AccountId, SetIndex) => Vec<ApprovalFlag>;
|
||||
/// The vote index and list slot that the candidate `who` was registered or `None` if they
|
||||
/// are not currently registered.
|
||||
pub RegisterInfoOf get(fn candidate_reg_info):
|
||||
map hasher(blake2_256) T::AccountId => Option<(VoteIndex, u32)>;
|
||||
map hasher(twox_64_concat) T::AccountId => Option<(VoteIndex, u32)>;
|
||||
/// Basic information about a voter.
|
||||
pub VoterInfoOf get(fn voter_info):
|
||||
map hasher(blake2_256) T::AccountId => Option<VoterInfo<BalanceOf<T>>>;
|
||||
map hasher(twox_64_concat) T::AccountId => Option<VoterInfo<BalanceOf<T>>>;
|
||||
/// The present voter list (chunked and capped at [`VOTER_SET_SIZE`]).
|
||||
pub Voters get(fn voters): map hasher(blake2_256) SetIndex => Vec<Option<T::AccountId>>;
|
||||
pub Voters get(fn voters): map hasher(twox_64_concat) SetIndex => Vec<Option<T::AccountId>>;
|
||||
/// the next free set to store a voter in. This will keep growing.
|
||||
pub NextVoterSet get(fn next_nonfull_voter_set): SetIndex = 0;
|
||||
/// Current number of Voters.
|
||||
@@ -266,7 +266,7 @@ decl_storage! {
|
||||
|
||||
/// Who is able to vote for whom. Value is the fund-holding account, key is the
|
||||
/// vote-transaction-sending account.
|
||||
pub Proxy get(fn proxy): map hasher(blake2_256) T::AccountId => Option<T::AccountId>;
|
||||
pub Proxy get(fn proxy): map hasher(blake2_128_concat) T::AccountId => Option<T::AccountId>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -132,9 +132,9 @@ pub trait Trait: frame_system::Trait + pallet_timestamp::Trait {
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as EVM {
|
||||
Accounts get(fn accounts) config(): map hasher(blake2_256) H160 => Account;
|
||||
AccountCodes: map hasher(blake2_256) H160 => Vec<u8>;
|
||||
AccountStorages: double_map hasher(blake2_256) H160, hasher(blake2_256) H256 => H256;
|
||||
Accounts get(fn accounts) config(): map hasher(blake2_128_concat) H160 => Account;
|
||||
AccountCodes: map hasher(blake2_128_concat) H160 => Vec<u8>;
|
||||
AccountStorages: double_map hasher(blake2_128_concat) H160, hasher(blake2_128_concat) H256 => H256;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -345,7 +345,7 @@ decl_storage! {
|
||||
// - `Foo::put(1); Foo::get()` returns `1`;
|
||||
// - `Foo::kill(); Foo::get()` returns `0` (u32::default()).
|
||||
// e.g. Foo: u32;
|
||||
// e.g. pub Bar get(fn bar): map hasher(blake2_256) T::AccountId => Vec<(T::Balance, u64)>;
|
||||
// e.g. pub Bar get(fn bar): map hasher(blake2_128_concat) T::AccountId => Vec<(T::Balance, u64)>;
|
||||
//
|
||||
// For basic value items, you'll get a type which implements
|
||||
// `frame_support::StorageValue`. For map items, you'll get a type which
|
||||
@@ -357,7 +357,7 @@ decl_storage! {
|
||||
Dummy get(fn dummy) config(): Option<T::Balance>;
|
||||
|
||||
// A map that has enumerable entries.
|
||||
Bar get(fn bar) config(): linked_map hasher(blake2_256) T::AccountId => T::Balance;
|
||||
Bar get(fn bar) config(): map hasher(blake2_128_concat) T::AccountId => T::Balance;
|
||||
|
||||
// this one uses the default, we'll demonstrate the usage of 'mutate' API.
|
||||
Foo get(fn foo) config(): T::Balance;
|
||||
@@ -731,7 +731,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -191,6 +191,7 @@ where
|
||||
digest,
|
||||
frame_system::InitKind::Full,
|
||||
);
|
||||
<frame_system::Module<System> as OnInitialize<System::BlockNumber>>::on_initialize(*block_number);
|
||||
<AllModules as OnInitialize<System::BlockNumber>>::on_initialize(*block_number);
|
||||
<frame_system::Module<System>>::register_extra_weight_unchecked(
|
||||
<AllModules as WeighBlock<System::BlockNumber>>::on_initialize(*block_number)
|
||||
@@ -249,11 +250,11 @@ where
|
||||
|
||||
/// Execute given extrinsics and take care of post-extrinsics book-keeping.
|
||||
fn execute_extrinsics_with_book_keeping(extrinsics: Vec<Block::Extrinsic>, block_number: NumberFor<Block>) {
|
||||
|
||||
extrinsics.into_iter().for_each(Self::apply_extrinsic_no_note);
|
||||
|
||||
// post-extrinsics book-keeping
|
||||
<frame_system::Module<System>>::note_finished_extrinsics();
|
||||
<frame_system::Module<System> as OnFinalize<System::BlockNumber>>::on_finalize(block_number);
|
||||
<AllModules as OnFinalize<System::BlockNumber>>::on_finalize(block_number);
|
||||
}
|
||||
|
||||
@@ -261,7 +262,9 @@ where
|
||||
/// except state-root.
|
||||
pub fn finalize_block() -> System::Header {
|
||||
<frame_system::Module<System>>::note_finished_extrinsics();
|
||||
<AllModules as OnFinalize<System::BlockNumber>>::on_finalize(<frame_system::Module<System>>::block_number());
|
||||
let block_number = <frame_system::Module<System>>::block_number();
|
||||
<frame_system::Module<System> as OnFinalize<System::BlockNumber>>::on_finalize(block_number);
|
||||
<AllModules as OnFinalize<System::BlockNumber>>::on_finalize(block_number);
|
||||
|
||||
// set up extrinsics
|
||||
<frame_system::Module<System>>::derive_extrinsics();
|
||||
@@ -478,7 +481,7 @@ mod tests {
|
||||
type Version = RuntimeVersion;
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
@@ -595,7 +598,7 @@ mod tests {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("e97d724f480f6e3215bd5c24b9ba51250e2514ac1c99e563fd77bfb9d6100b1c").into(),
|
||||
state_root: hex!("489ae9b57a19bb4733a264dc64bbcae9b140a904657a681ed3bb5fbbe8cf412b").into(),
|
||||
extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
|
||||
@@ -26,8 +26,6 @@ use frame_support::traits::Get;
|
||||
use frame_system::{ensure_none, Trait as SystemTrait};
|
||||
use sp_finality_tracker::{INHERENT_IDENTIFIER, FinalizedInherentData};
|
||||
|
||||
mod migration;
|
||||
|
||||
pub const DEFAULT_WINDOW_SIZE: u32 = 101;
|
||||
pub const DEFAULT_REPORT_LATENCY: u32 = 1000;
|
||||
|
||||
@@ -91,10 +89,6 @@ decl_module! {
|
||||
fn on_finalize() {
|
||||
Self::update_hint(<Self as Store>::Update::take())
|
||||
}
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
migration::on_runtime_upgrade::<T>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +262,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Migration code to update storage.
|
||||
|
||||
use super::*;
|
||||
use frame_support::storage::migration::{put_storage_value, take_storage_value};
|
||||
|
||||
pub fn on_runtime_upgrade<T: Trait>() {
|
||||
change_name_timestamp_to_finality_tracker::<T>()
|
||||
}
|
||||
|
||||
// Change the storage name used by this pallet from `Timestamp` to `FinalityTracker`.
|
||||
//
|
||||
// Since the format of the storage items themselves have not changed, we do not
|
||||
// need to keep track of a storage version. If the runtime does not need to be
|
||||
// upgraded, nothing here will happen anyway.
|
||||
|
||||
fn change_name_timestamp_to_finality_tracker<T:Trait>() {
|
||||
sp_runtime::print("Migrating Finality Tracker.");
|
||||
|
||||
if let Some(recent_hints) = take_storage_value::<Vec<T::BlockNumber>>(b"Timestamp", b"RecentHints", &[]) {
|
||||
put_storage_value(b"FinalityTracker", b"RecentHints", &[], recent_hints);
|
||||
}
|
||||
|
||||
if let Some(ordered_hints) = take_storage_value::<Vec<T::BlockNumber>>(b"Timestamp", b"OrderedHints", &[]) {
|
||||
put_storage_value(b"FinalityTracker", b"OrderedHints", &[], ordered_hints);
|
||||
}
|
||||
|
||||
if let Some(median) = take_storage_value::<T::BlockNumber>(b"Timestamp", b"Median", &[]) {
|
||||
put_storage_value(b"FinalityTracker", b"Median", &[], median);
|
||||
}
|
||||
|
||||
if let Some(update) = take_storage_value::<T::BlockNumber>(b"Timestamp", b"Update", &[]) {
|
||||
put_storage_value(b"FinalityTracker", b"Update", &[], update);
|
||||
}
|
||||
|
||||
if let Some(initialized) = take_storage_value::<bool>(b"Timestamp", b"Initialized", &[]) {
|
||||
put_storage_value(b"FinalityTracker", b"Initialized", &[], initialized);
|
||||
}
|
||||
}
|
||||
@@ -439,26 +439,26 @@ decl_storage! {
|
||||
pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig<T>| {
|
||||
let issuance = config.initial_balance * (config.endowed_accounts.len() as u32).into();
|
||||
config.assets.iter().map(|id| (id.clone(), issuance)).collect::<Vec<_>>()
|
||||
}): map hasher(blake2_256) T::AssetId => T::Balance;
|
||||
}): map hasher(twox_64_concat) T::AssetId => T::Balance;
|
||||
|
||||
/// The free balance of a given asset under an account.
|
||||
pub FreeBalance:
|
||||
double_map hasher(blake2_256) T::AssetId, hasher(twox_128) T::AccountId => T::Balance;
|
||||
double_map hasher(twox_64_concat) T::AssetId, hasher(blake2_128_concat) T::AccountId => T::Balance;
|
||||
|
||||
/// The reserved balance of a given asset under an account.
|
||||
pub ReservedBalance:
|
||||
double_map hasher(blake2_256) T::AssetId, hasher(twox_128) T::AccountId => T::Balance;
|
||||
double_map hasher(twox_64_concat) T::AssetId, hasher(blake2_128_concat) T::AccountId => T::Balance;
|
||||
|
||||
/// Next available ID for user-created asset.
|
||||
pub NextAssetId get(fn next_asset_id) config(): T::AssetId;
|
||||
|
||||
/// Permission options for a given asset.
|
||||
pub Permissions get(fn get_permission):
|
||||
map hasher(blake2_256) T::AssetId => PermissionVersions<T::AccountId>;
|
||||
map hasher(twox_64_concat) T::AssetId => PermissionVersions<T::AccountId>;
|
||||
|
||||
/// Any liquidity locks on some account balances.
|
||||
pub Locks get(fn locks):
|
||||
map hasher(blake2_256) T::AccountId => Vec<BalanceLock<T::Balance>>;
|
||||
map hasher(blake2_128_concat) T::AccountId => Vec<BalanceLock<T::Balance>>;
|
||||
|
||||
/// The identity of the asset which is the one that is designated for the chain's staking system.
|
||||
pub StakingAssetId get(fn staking_asset_id) config(): T::AssetId;
|
||||
@@ -1123,7 +1123,7 @@ impl<T: Subtrait> frame_system::Trait for ElevatedTrait<T> {
|
||||
type Version = T::Version;
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
impl<T: Subtrait> Trait for ElevatedTrait<T> {
|
||||
|
||||
@@ -63,7 +63,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -174,8 +174,9 @@ decl_storage! {
|
||||
/// in the "set" of Grandpa validators from genesis.
|
||||
CurrentSetId get(fn current_set_id) build(|_| fg_primitives::SetId::default()): SetId;
|
||||
|
||||
/// A mapping from grandpa set ID to the index of the *most recent* session for which its members were responsible.
|
||||
SetIdSession get(fn session_for_set): map hasher(blake2_256) SetId => Option<SessionIndex>;
|
||||
/// A mapping from grandpa set ID to the index of the *most recent* session for which its
|
||||
/// members were responsible.
|
||||
SetIdSession get(fn session_for_set): map hasher(twox_64_concat) SetId => Option<SessionIndex>;
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(authorities): AuthorityList;
|
||||
@@ -183,6 +184,15 @@ decl_storage! {
|
||||
}
|
||||
}
|
||||
|
||||
mod migration {
|
||||
use super::*;
|
||||
pub fn migrate<T: Trait>() {
|
||||
for i in 0..=CurrentSetId::get() {
|
||||
SetIdSession::migrate_key_from_blake(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
type Error = Error<T>;
|
||||
@@ -195,6 +205,10 @@ decl_module! {
|
||||
// FIXME: https://github.com/paritytech/substrate/issues/1112
|
||||
}
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
migration::migrate::<T>();
|
||||
}
|
||||
|
||||
fn on_initialize() {
|
||||
#[cfg(feature = "migrate-authorities")]
|
||||
Self::migrate_authorities();
|
||||
|
||||
@@ -66,7 +66,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -77,10 +77,10 @@ use frame_support::{
|
||||
weights::SimpleDispatchInfo,
|
||||
};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
use frame_support::traits::MigrateAccount;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub mod benchmarking;
|
||||
mod migration;
|
||||
|
||||
type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
|
||||
type NegativeImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::NegativeImbalance;
|
||||
@@ -386,18 +386,18 @@ decl_storage! {
|
||||
trait Store for Module<T: Trait> as Identity {
|
||||
/// Information that is pertinent to identify the entity behind an account.
|
||||
pub IdentityOf get(fn identity):
|
||||
map hasher(blake2_256) T::AccountId => Option<Registration<BalanceOf<T>>>;
|
||||
map hasher(twox_64_concat) T::AccountId => Option<Registration<BalanceOf<T>>>;
|
||||
|
||||
/// The super-identity of an alternative "sub" identity together with its name, within that
|
||||
/// context. If the account is not some other account's sub-identity, then just `None`.
|
||||
pub SuperOf get(fn super_of):
|
||||
map hasher(blake2_256) T::AccountId => Option<(T::AccountId, Data)>;
|
||||
map hasher(blake2_128_concat) T::AccountId => Option<(T::AccountId, Data)>;
|
||||
|
||||
/// Alternative "sub" identities of this account.
|
||||
///
|
||||
/// The first item is the deposit, the second is a vector of the accounts.
|
||||
pub SubsOf get(fn subs_of):
|
||||
map hasher(blake2_256) T::AccountId => (BalanceOf<T>, Vec<T::AccountId>);
|
||||
map hasher(twox_64_concat) T::AccountId => (BalanceOf<T>, Vec<T::AccountId>);
|
||||
|
||||
/// The set of registrars. Not expected to get very big as can only be added through a
|
||||
/// special origin (likely a council motion).
|
||||
@@ -874,10 +874,6 @@ decl_module! {
|
||||
|
||||
Self::deposit_event(RawEvent::IdentityKilled(target, deposit));
|
||||
}
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
migration::on_runtime_upgrade::<T>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -891,6 +887,18 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> MigrateAccount<T::AccountId> for Module<T> {
|
||||
fn migrate_account(a: &T::AccountId) {
|
||||
if IdentityOf::<T>::migrate_key_from_blake(a).is_some() {
|
||||
if let Some((_, subs)) = SubsOf::<T>::migrate_key_from_blake(a) {
|
||||
for sub in subs.into_iter() {
|
||||
SuperOf::<T>::migrate_key_from_blake(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -941,7 +949,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Migration code to update storage.
|
||||
|
||||
use super::*;
|
||||
use frame_support::storage::migration::{put_storage_value, take_storage_value, StorageIterator};
|
||||
|
||||
pub fn on_runtime_upgrade<T: Trait>() {
|
||||
change_name_sudo_to_identity::<T>()
|
||||
}
|
||||
|
||||
// Change the storage name used by this pallet from `Sudo` to `Identity`.
|
||||
//
|
||||
// Since the format of the storage items themselves have not changed, we do not
|
||||
// need to keep track of a storage version. If the runtime does not need to be
|
||||
// upgraded, nothing here will happen anyway.
|
||||
|
||||
fn change_name_sudo_to_identity<T: Trait>() {
|
||||
sp_runtime::print("Migrating Identity.");
|
||||
|
||||
for (hash, identity_of) in StorageIterator::<Registration<BalanceOf<T>>>::new(b"Sudo", b"IdentityOf").drain() {
|
||||
put_storage_value(b"Identity", b"IdentityOf", &hash, identity_of);
|
||||
}
|
||||
|
||||
for (hash, super_of) in StorageIterator::<(T::AccountId, Data)>::new(b"Sudo", b"SuperOf").drain() {
|
||||
put_storage_value(b"Identity", b"SuperOf", &hash, super_of);
|
||||
}
|
||||
|
||||
for (hash, subs_of) in StorageIterator::<(BalanceOf<T>, Vec<T::AccountId>)>::new(b"Sudo", b"SubsOf").drain() {
|
||||
put_storage_value(b"Identity", b"SubsOf", &hash, subs_of);
|
||||
}
|
||||
|
||||
if let Some(registrars) = take_storage_value::<Vec<Option<RegistrarInfo<BalanceOf<T>, T::AccountId>>>>(b"Sudo", b"Registrars", &[]) {
|
||||
put_storage_value(b"Identity", b"Registrars", &[], registrars);
|
||||
}
|
||||
|
||||
sp_runtime::print("Done Identity.");
|
||||
}
|
||||
@@ -95,6 +95,7 @@ use frame_support::{
|
||||
};
|
||||
use frame_system::{self as system, ensure_none};
|
||||
use frame_system::offchain::SubmitUnsignedTransaction;
|
||||
use frame_support::traits::MigrateAccount;
|
||||
|
||||
pub mod sr25519 {
|
||||
mod app_sr25519 {
|
||||
@@ -274,16 +275,17 @@ decl_storage! {
|
||||
/// The current set of keys that may issue a heartbeat.
|
||||
Keys get(fn keys): Vec<T::AuthorityId>;
|
||||
|
||||
/// For each session index, we keep a mapping of `AuthIndex`
|
||||
/// to `offchain::OpaqueNetworkState`.
|
||||
/// For each session index, we keep a mapping of `AuthIndex` to
|
||||
/// `offchain::OpaqueNetworkState`.
|
||||
ReceivedHeartbeats get(fn received_heartbeats):
|
||||
double_map hasher(blake2_256) SessionIndex, hasher(blake2_256) AuthIndex
|
||||
double_map hasher(twox_64_concat) SessionIndex, hasher(twox_64_concat) AuthIndex
|
||||
=> Option<Vec<u8>>;
|
||||
|
||||
/// For each session index, we keep a mapping of `T::ValidatorId` to the
|
||||
/// number of blocks authored by the given authority.
|
||||
AuthoredBlocks get(fn authored_blocks):
|
||||
double_map hasher(blake2_256) SessionIndex, hasher(blake2_256) T::ValidatorId => u32;
|
||||
double_map hasher(twox_64_concat) SessionIndex, hasher(twox_64_concat) T::ValidatorId
|
||||
=> u32;
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(keys): Vec<T::AuthorityId>;
|
||||
@@ -301,12 +303,38 @@ decl_error! {
|
||||
}
|
||||
}
|
||||
|
||||
mod migration {
|
||||
use super::*;
|
||||
use frame_support::Blake2_256;
|
||||
pub fn migrate<T: Trait>() {
|
||||
let current_index = <pallet_session::Module<T>>::current_index();
|
||||
let key_count = Keys::<T>::get().len() as AuthIndex;
|
||||
for i in 0..key_count {
|
||||
ReceivedHeartbeats::migrate_keys::<Blake2_256, Blake2_256, _, _>(current_index, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> MigrateAccount<T::AccountId> for Module<T> {
|
||||
fn migrate_account(a: &T::AccountId) {
|
||||
use frame_support::Blake2_256;
|
||||
let current_index = <pallet_session::Module<T>>::current_index();
|
||||
if let Ok(v) = a.using_encoded(|mut d| T::ValidatorId::decode(&mut d)) {
|
||||
AuthoredBlocks::<T>::migrate_keys::<Blake2_256, Blake2_256, _, _>(current_index, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
type Error = Error<T>;
|
||||
|
||||
fn deposit_event() = default;
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
migration::migrate::<T>();
|
||||
}
|
||||
|
||||
fn heartbeat(
|
||||
origin,
|
||||
heartbeat: Heartbeat<T::BlockNumber>,
|
||||
|
||||
@@ -119,7 +119,7 @@ impl frame_system::Trait for Runtime {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -321,7 +321,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
ord_parameter_types! {
|
||||
|
||||
@@ -277,6 +277,7 @@ pub enum StorageHasher {
|
||||
Twox128,
|
||||
Twox256,
|
||||
Twox64Concat,
|
||||
Identity,
|
||||
}
|
||||
|
||||
/// A storage entry type.
|
||||
@@ -288,7 +289,8 @@ pub enum StorageEntryType {
|
||||
hasher: StorageHasher,
|
||||
key: DecodeDifferentStr,
|
||||
value: DecodeDifferentStr,
|
||||
is_linked: bool,
|
||||
// is_linked flag previously, unused now to keep backwards compat
|
||||
unused: bool,
|
||||
},
|
||||
DoubleMap {
|
||||
hasher: StorageHasher,
|
||||
|
||||
@@ -78,7 +78,7 @@ pub trait Trait: frame_system::Trait {
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Nicks {
|
||||
/// The lookup table for names.
|
||||
NameOf: map hasher(blake2_256) T::AccountId => Option<(Vec<u8>, BalanceOf<T>)>;
|
||||
NameOf: map hasher(twox_64_concat) T::AccountId => Option<(Vec<u8>, BalanceOf<T>)>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -54,11 +54,13 @@ pub trait Trait: frame_system::Trait {
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Offences {
|
||||
/// The primary structure that holds all offence records keyed by report identifiers.
|
||||
Reports get(fn reports): map hasher(blake2_256) ReportIdOf<T> => Option<OffenceDetails<T::AccountId, T::IdentificationTuple>>;
|
||||
Reports get(fn reports):
|
||||
map hasher(twox_64_concat) ReportIdOf<T>
|
||||
=> Option<OffenceDetails<T::AccountId, T::IdentificationTuple>>;
|
||||
|
||||
/// A vector of reports of the same kind that happened at the same time slot.
|
||||
ConcurrentReportsIndex:
|
||||
double_map hasher(blake2_256) Kind, hasher(blake2_256) OpaqueTimeSlot
|
||||
double_map hasher(twox_64_concat) Kind, hasher(twox_64_concat) OpaqueTimeSlot
|
||||
=> Vec<ReportIdOf<T>>;
|
||||
|
||||
/// Enumerates all reports of a kind along with the time they happened.
|
||||
@@ -67,7 +69,7 @@ decl_storage! {
|
||||
///
|
||||
/// Note that the actual type of this mapping is `Vec<u8>`, this is because values of
|
||||
/// different types are not supported at the moment so we are doing the manual serialization.
|
||||
ReportsByKindIndex: map hasher(blake2_256) Kind => Vec<u8>; // (O::TimeSlot, ReportIdOf<T>)
|
||||
ReportsByKindIndex: map hasher(twox_64_concat) Kind => Vec<u8>; // (O::TimeSlot, ReportIdOf<T>)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,8 +85,15 @@ decl_module! {
|
||||
/// Offences module, currently just responsible for taking offence reports.
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn deposit_event() = default;
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
Reports::<T>::remove_all();
|
||||
ConcurrentReportsIndex::<T>::remove_all();
|
||||
ReportsByKindIndex::remove_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait, O: Offence<T::IdentificationTuple>>
|
||||
ReportOffence<T::AccountId, T::IdentificationTuple, O> for Module<T>
|
||||
where
|
||||
|
||||
@@ -90,7 +90,7 @@ impl frame_system::Trait for Runtime {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@ use frame_support::{
|
||||
traits::{Currency, ReservableCurrency, Get, BalanceStatus},
|
||||
};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
use frame_support::traits::MigrateAccount;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
@@ -238,7 +239,7 @@ decl_storage! {
|
||||
trait Store for Module<T: Trait> as Recovery {
|
||||
/// The set of recoverable accounts and their recovery configuration.
|
||||
pub Recoverable get(fn recovery_config):
|
||||
map hasher(blake2_256) T::AccountId
|
||||
map hasher(twox_64_concat) T::AccountId
|
||||
=> Option<RecoveryConfig<T::BlockNumber, BalanceOf<T>, T::AccountId>>;
|
||||
|
||||
/// Active recovery attempts.
|
||||
@@ -253,7 +254,14 @@ decl_storage! {
|
||||
///
|
||||
/// Map from the user who can access it to the recovered account.
|
||||
pub Proxy get(fn proxy):
|
||||
map hasher(blake2_256) T::AccountId => Option<T::AccountId>;
|
||||
map hasher(blake2_128_concat) T::AccountId => Option<T::AccountId>;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> MigrateAccount<T::AccountId> for Module<T> {
|
||||
fn migrate_account(a: &T::AccountId) {
|
||||
Recoverable::<T>::migrate_key_from_blake(a);
|
||||
Proxy::<T>::migrate_key_from_blake(a);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u128>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ decl_storage! {
|
||||
/// check if a candidate is already in the pool, without having to
|
||||
/// iterate over the entire pool (the `Pool` is not sorted by
|
||||
/// `T::AccountId`, but by `T::Score` instead).
|
||||
CandidateExists get(fn candidate_exists): map hasher(blake2_256) T::AccountId => bool;
|
||||
CandidateExists get(fn candidate_exists): map hasher(twox_64_concat) T::AccountId => bool;
|
||||
|
||||
/// The current membership, stored as an ordered Vec.
|
||||
Members get(fn members): Vec<T::AccountId>;
|
||||
|
||||
@@ -71,7 +71,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,12 +56,12 @@ decl_storage! {
|
||||
trait Store for Module<T: Trait> as Session {
|
||||
/// Mapping from historical session indices to session-data root hash and validator count.
|
||||
HistoricalSessions get(fn historical_root):
|
||||
map hasher(blake2_256) SessionIndex => Option<(T::Hash, ValidatorCount)>;
|
||||
map hasher(twox_64_concat) SessionIndex => Option<(T::Hash, ValidatorCount)>;
|
||||
/// The range of historical sessions we store. [first, last)
|
||||
StoredRange: Option<(SessionIndex, SessionIndex)>;
|
||||
/// Deprecated.
|
||||
CachedObsolete:
|
||||
map hasher(blake2_256) SessionIndex
|
||||
map hasher(twox_64_concat) SessionIndex
|
||||
=> Option<Vec<(T::ValidatorId, T::FullIdentification)>>;
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,20 @@ decl_module! {
|
||||
fn on_initialize(_n: T::BlockNumber) {
|
||||
CachedObsolete::<T>::remove_all();
|
||||
}
|
||||
fn on_runtime_upgrade() {
|
||||
migration::migrate::<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod migration {
|
||||
use super::*;
|
||||
pub fn migrate<T: Trait>() {
|
||||
if let Some((begin, end)) = StoredRange::get() {
|
||||
for i in begin..end {
|
||||
HistoricalSessions::<T>::migrate_key_from_blake(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ use frame_support::{ensure, decl_module, decl_event, decl_storage, decl_error, C
|
||||
use frame_support::{traits::{Get, FindAuthor, ValidatorRegistration}, Parameter};
|
||||
use frame_support::dispatch::{self, DispatchResult, DispatchError};
|
||||
use frame_system::{self as system, ensure_signed};
|
||||
use frame_support::traits::MigrateAccount;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
@@ -364,10 +365,10 @@ decl_storage! {
|
||||
DisabledValidators get(fn disabled_validators): Vec<u32>;
|
||||
|
||||
/// The next session keys for a validator.
|
||||
NextKeys: map hasher(blake2_256) T::ValidatorId => Option<T::Keys>;
|
||||
NextKeys: map hasher(twox_64_concat) T::ValidatorId => Option<T::Keys>;
|
||||
|
||||
/// The owner of a key. The key is the `KeyTypeId` + the encoded key.
|
||||
KeyOwner: map hasher(blake2_256) (KeyTypeId, Vec<u8>) => Option<T::ValidatorId>;
|
||||
KeyOwner: map hasher(twox_64_concat) (KeyTypeId, Vec<u8>) => Option<T::ValidatorId>;
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(keys): Vec<(T::AccountId, T::ValidatorId, T::Keys)>;
|
||||
@@ -498,36 +499,22 @@ decl_module! {
|
||||
Self::rotate_session();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when the runtime is upgraded.
|
||||
fn on_runtime_upgrade() {
|
||||
Self::migrate();
|
||||
impl<T: Trait> MigrateAccount<T::AccountId> for Module<T> {
|
||||
fn migrate_account(a: &T::AccountId) {
|
||||
if let Some(v) = T::ValidatorIdOf::convert(a.clone()) {
|
||||
if let Some(keys) = NextKeys::<T>::migrate_key_from_blake(v) {
|
||||
for id in T::Keys::key_ids() {
|
||||
KeyOwner::<T>::migrate_key_from_blake((*id, keys.get_raw(*id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Move keys from NextKeys and KeyOwner, if any exist.
|
||||
fn migrate() {
|
||||
use frame_support::storage::migration::{put_storage_value, StorageIterator};
|
||||
sp_runtime::print("Migrating session's double-maps...");
|
||||
|
||||
let prefix = {
|
||||
const DEDUP_KEY_PREFIX: &[u8] = b":session:keys";
|
||||
let encoded_prefix_key_hash = codec::Encode::encode(&DEDUP_KEY_PREFIX);
|
||||
let mut h = sp_io::hashing::twox_64(&encoded_prefix_key_hash[..]).to_vec();
|
||||
h.extend(&encoded_prefix_key_hash[..]);
|
||||
h
|
||||
};
|
||||
|
||||
for (hash, value) in StorageIterator::<T::Keys>::with_suffix(b"Session", b"NextKeys", &prefix[..]).drain() {
|
||||
put_storage_value(b"Session", b"NextKeys", &hash, value);
|
||||
}
|
||||
for (hash, value) in StorageIterator::<T::ValidatorId>::with_suffix(b"Session", b"KeyOwner", &prefix[..]).drain() {
|
||||
put_storage_value(b"Session", b"KeyOwner", &hash, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Move on to next session. Register new validator set and session keys. Changes
|
||||
/// to the validator set have a session of delay to take effect. This allows for
|
||||
/// equivocation punishment after a fork.
|
||||
|
||||
@@ -179,7 +179,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -261,10 +261,7 @@ use sp_runtime::{Percent, ModuleId, RuntimeDebug,
|
||||
};
|
||||
use frame_support::{decl_error, decl_module, decl_storage, decl_event, ensure, dispatch::DispatchResult};
|
||||
use frame_support::weights::SimpleDispatchInfo;
|
||||
use frame_support::traits::{
|
||||
Currency, ReservableCurrency, Randomness, Get, ChangeMembers, BalanceStatus,
|
||||
ExistenceRequirement::AllowDeath,
|
||||
};
|
||||
use frame_support::traits::{Currency, ReservableCurrency, Randomness, Get, ChangeMembers, BalanceStatus, ExistenceRequirement::AllowDeath, MigrateAccount};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
|
||||
type BalanceOf<T, I> = <<T as Trait<I>>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
|
||||
@@ -414,7 +411,7 @@ decl_storage! {
|
||||
|
||||
/// The set of suspended candidates.
|
||||
pub SuspendedCandidates get(suspended_candidate):
|
||||
map hasher(blake2_256) T::AccountId
|
||||
map hasher(twox_64_concat) T::AccountId
|
||||
=> Option<(BalanceOf<T, I>, BidKind<T::AccountId, BalanceOf<T, I>>)>;
|
||||
|
||||
/// Amount of our account balance that is specifically for the next round's bid(s).
|
||||
@@ -432,19 +429,19 @@ decl_storage! {
|
||||
}): Vec<T::AccountId>;
|
||||
|
||||
/// The set of suspended members.
|
||||
pub SuspendedMembers get(fn suspended_member): map hasher(blake2_256) T::AccountId => bool;
|
||||
pub SuspendedMembers get(fn suspended_member): map hasher(twox_64_concat) T::AccountId => bool;
|
||||
|
||||
/// The current bids, stored ordered by the value of the bid.
|
||||
Bids: Vec<Bid<T::AccountId, BalanceOf<T, I>>>;
|
||||
|
||||
/// Members currently vouching or banned from vouching again
|
||||
Vouching get(fn vouching): map hasher(blake2_256) T::AccountId => Option<VouchingStatus>;
|
||||
Vouching get(fn vouching): map hasher(twox_64_concat) T::AccountId => Option<VouchingStatus>;
|
||||
|
||||
/// Pending payouts; ordered by block number, with the amount that should be paid out.
|
||||
Payouts: map hasher(blake2_256) T::AccountId => Vec<(T::BlockNumber, BalanceOf<T, I>)>;
|
||||
Payouts: map hasher(twox_64_concat) T::AccountId => Vec<(T::BlockNumber, BalanceOf<T, I>)>;
|
||||
|
||||
/// The ongoing number of losing votes cast by the member.
|
||||
Strikes: map hasher(blake2_256) T::AccountId => StrikeCount;
|
||||
Strikes: map hasher(twox_64_concat) T::AccountId => StrikeCount;
|
||||
|
||||
/// Double map from Candidate -> Voter -> (Maybe) Vote.
|
||||
Votes: double_map
|
||||
@@ -1133,6 +1130,12 @@ decl_event! {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> MigrateAccount<T::AccountId> for Module<T> {
|
||||
fn migrate_account(a: &T::AccountId) {
|
||||
Payouts::<T>::migrate_key_from_blake(a);
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple ensure origin struct to filter for the founder account.
|
||||
pub struct EnsureFounder<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Trait> EnsureOrigin<T::Origin> for EnsureFounder<T> {
|
||||
|
||||
@@ -77,7 +77,7 @@ impl frame_system::Trait for Test {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
}
|
||||
|
||||
@@ -254,19 +254,16 @@ mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod slashing;
|
||||
mod migration;
|
||||
|
||||
pub mod inflation;
|
||||
|
||||
use sp_std::{prelude::*, result, collections::btree_map::BTreeMap};
|
||||
use codec::{HasCompact, Encode, Decode};
|
||||
use frame_support::{
|
||||
decl_module, decl_event, decl_storage, ensure, decl_error,
|
||||
weights::SimpleDispatchInfo,
|
||||
dispatch::DispatchResult,
|
||||
traits::{
|
||||
Currency, LockIdentifier, LockableCurrency,
|
||||
WithdrawReasons, OnUnbalanced, Imbalance, Get, Time
|
||||
decl_module, decl_event, decl_storage, ensure, decl_error, weights::SimpleDispatchInfo,
|
||||
dispatch::DispatchResult, storage::IterableStorageMap, traits::{
|
||||
Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get,
|
||||
Time
|
||||
}
|
||||
};
|
||||
use pallet_session::historical::SessionManager;
|
||||
@@ -287,6 +284,7 @@ use sp_runtime::{Serialize, Deserialize};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
|
||||
use sp_phragmen::ExtendedBalance;
|
||||
use frame_support::traits::MigrateAccount;
|
||||
|
||||
const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4;
|
||||
const MAX_NOMINATIONS: usize = 16;
|
||||
@@ -685,13 +683,13 @@ impl Default for Forcing {
|
||||
// storage migration logic. This should match directly with the semantic versions of the Rust crate.
|
||||
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)]
|
||||
enum Releases {
|
||||
V1_0_0,
|
||||
V1_0_0Ancient,
|
||||
V2_0_0,
|
||||
}
|
||||
|
||||
impl Default for Releases {
|
||||
fn default() -> Self {
|
||||
Releases::V1_0_0
|
||||
Releases::V2_0_0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,23 +717,23 @@ decl_storage! {
|
||||
pub Invulnerables get(fn invulnerables) config(): Vec<T::AccountId>;
|
||||
|
||||
/// Map from all locked "stash" accounts to the controller account.
|
||||
pub Bonded get(fn bonded): map hasher(blake2_256) T::AccountId => Option<T::AccountId>;
|
||||
pub Bonded get(fn bonded): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;
|
||||
|
||||
/// Map from all (unlocked) "controller" accounts to the info regarding the staking.
|
||||
pub Ledger get(fn ledger):
|
||||
map hasher(blake2_256) T::AccountId
|
||||
map hasher(blake2_128_concat) T::AccountId
|
||||
=> Option<StakingLedger<T::AccountId, BalanceOf<T>>>;
|
||||
|
||||
/// Where the reward payment should be made. Keyed by stash.
|
||||
pub Payee get(fn payee): map hasher(blake2_256) T::AccountId => RewardDestination;
|
||||
pub Payee get(fn payee): map hasher(twox_64_concat) T::AccountId => RewardDestination;
|
||||
|
||||
/// The map from (wannabe) validator stash key to the preferences of that validator.
|
||||
pub Validators get(fn validators):
|
||||
linked_map hasher(blake2_256) T::AccountId => ValidatorPrefs;
|
||||
map hasher(twox_64_concat) T::AccountId => ValidatorPrefs;
|
||||
|
||||
/// The map from nominator stash key to the set of stash keys of all validators to nominate.
|
||||
pub Nominators get(fn nominators):
|
||||
linked_map hasher(blake2_256) T::AccountId => Option<Nominations<T::AccountId>>;
|
||||
map hasher(twox_64_concat) T::AccountId => Option<Nominations<T::AccountId>>;
|
||||
|
||||
/// The current era index.
|
||||
///
|
||||
@@ -751,7 +749,7 @@ decl_storage! {
|
||||
|
||||
/// The session index at which the era start for the last `HISTORY_DEPTH` eras
|
||||
pub ErasStartSessionIndex get(fn eras_start_session_index):
|
||||
map hasher(blake2_256) EraIndex => Option<SessionIndex>;
|
||||
map hasher(twox_64_concat) EraIndex => Option<SessionIndex>;
|
||||
|
||||
/// Exposure of validator at era.
|
||||
///
|
||||
@@ -780,7 +778,7 @@ decl_storage! {
|
||||
|
||||
/// Similarly to `ErasStakers` this holds the preferences of validators.
|
||||
///
|
||||
/// This is keyed fist by the era index to allow bulk deletion and then the stash account.
|
||||
/// This is keyed first by the era index to allow bulk deletion and then the stash account.
|
||||
///
|
||||
/// Is it removed after `HISTORY_DEPTH` eras.
|
||||
// If prefs hasn't been set or has been removed then 0 commission is returned.
|
||||
@@ -792,17 +790,17 @@ decl_storage! {
|
||||
///
|
||||
/// Eras that haven't finished yet or has been removed doesn't have reward.
|
||||
pub ErasValidatorReward get(fn eras_validator_reward):
|
||||
map hasher(blake2_256) EraIndex => Option<BalanceOf<T>>;
|
||||
map hasher(twox_64_concat) EraIndex => Option<BalanceOf<T>>;
|
||||
|
||||
/// Rewards for the last `HISTORY_DEPTH` eras.
|
||||
/// If reward hasn't been set or has been removed then 0 reward is returned.
|
||||
pub ErasRewardPoints get(fn eras_reward_points):
|
||||
map hasher(blake2_256) EraIndex => EraRewardPoints<T::AccountId>;
|
||||
map hasher(twox_64_concat) EraIndex => EraRewardPoints<T::AccountId>;
|
||||
|
||||
/// The total amount staked for the last `HISTORY_DEPTH` eras.
|
||||
/// If total hasn't been set or has been removed then 0 stake is returned.
|
||||
pub ErasTotalStake get(fn eras_total_stake):
|
||||
map hasher(blake2_256) EraIndex => BalanceOf<T>;
|
||||
map hasher(twox_64_concat) EraIndex => BalanceOf<T>;
|
||||
|
||||
/// True if the next session change will be a new era regardless of index.
|
||||
pub ForceEra get(fn force_era) config(): Forcing;
|
||||
@@ -818,7 +816,7 @@ decl_storage! {
|
||||
|
||||
/// All unapplied slashes that are queued for later.
|
||||
pub UnappliedSlashes:
|
||||
map hasher(blake2_256) EraIndex => Vec<UnappliedSlash<T::AccountId, BalanceOf<T>>>;
|
||||
map hasher(twox_64_concat) EraIndex => Vec<UnappliedSlash<T::AccountId, BalanceOf<T>>>;
|
||||
|
||||
/// A mapping from still-bonded eras to the first session index of that era.
|
||||
///
|
||||
@@ -829,21 +827,21 @@ decl_storage! {
|
||||
/// All slashing events on validators, mapped by era to the highest slash proportion
|
||||
/// and slash value of the era.
|
||||
ValidatorSlashInEra:
|
||||
double_map hasher(blake2_256) EraIndex, hasher(twox_128) T::AccountId
|
||||
double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId
|
||||
=> Option<(Perbill, BalanceOf<T>)>;
|
||||
|
||||
/// All slashing events on nominators, mapped by era to the highest slash value of the era.
|
||||
NominatorSlashInEra:
|
||||
double_map hasher(blake2_256) EraIndex, hasher(twox_128) T::AccountId
|
||||
double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId
|
||||
=> Option<BalanceOf<T>>;
|
||||
|
||||
/// Slashing spans for stash accounts.
|
||||
SlashingSpans: map hasher(blake2_256) T::AccountId => Option<slashing::SlashingSpans>;
|
||||
SlashingSpans: map hasher(twox_64_concat) T::AccountId => Option<slashing::SlashingSpans>;
|
||||
|
||||
/// Records information about the maximum slash of a stash within a slashing span,
|
||||
/// as well as how much reward has been paid out.
|
||||
SpanSlash:
|
||||
map hasher(blake2_256) (T::AccountId, slashing::SpanIndex)
|
||||
map hasher(twox_64_concat) (T::AccountId, slashing::SpanIndex)
|
||||
=> slashing::SpanRecord<BalanceOf<T>>;
|
||||
|
||||
/// The earliest era for which we have a pending, unapplied slash.
|
||||
@@ -957,7 +955,7 @@ decl_module! {
|
||||
fn deposit_event() = default;
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
migration::on_runtime_upgrade::<T>();
|
||||
migrate::<T>();
|
||||
}
|
||||
|
||||
fn on_finalize() {
|
||||
@@ -1480,6 +1478,30 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> MigrateAccount<T::AccountId> for Module<T> {
|
||||
fn migrate_account(a: &T::AccountId) {
|
||||
if let Some(controller) = Bonded::<T>::migrate_key_from_blake(a) {
|
||||
Ledger::<T>::migrate_key_from_blake(controller);
|
||||
Payee::<T>::migrate_key_from_blake(a);
|
||||
Validators::<T>::migrate_key_from_blake(a);
|
||||
Nominators::<T>::migrate_key_from_blake(a);
|
||||
SlashingSpans::<T>::migrate_key_from_blake(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn migrate<T: Trait>() {
|
||||
if let Some(current_era) = CurrentEra::get() {
|
||||
let history_depth = HistoryDepth::get();
|
||||
for era in current_era.saturating_sub(history_depth)..=current_era {
|
||||
ErasStartSessionIndex::migrate_key_from_blake(era);
|
||||
ErasValidatorReward::<T>::migrate_key_from_blake(era);
|
||||
ErasRewardPoints::<T>::migrate_key_from_blake(era);
|
||||
ErasTotalStake::<T>::migrate_key_from_blake(era);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
// PUBLIC IMMUTABLES
|
||||
|
||||
@@ -1823,14 +1845,14 @@ impl<T: Trait> Module<T> {
|
||||
let mut all_nominators: Vec<(T::AccountId, Vec<T::AccountId>)> = Vec::new();
|
||||
let mut all_validators_and_prefs = BTreeMap::new();
|
||||
let mut all_validators = Vec::new();
|
||||
for (validator, preference) in <Validators<T>>::enumerate() {
|
||||
for (validator, preference) in <Validators<T>>::iter() {
|
||||
let self_vote = (validator.clone(), vec![validator.clone()]);
|
||||
all_nominators.push(self_vote);
|
||||
all_validators_and_prefs.insert(validator.clone(), preference);
|
||||
all_validators.push(validator);
|
||||
}
|
||||
|
||||
let nominator_votes = <Nominators<T>>::enumerate().map(|(nominator, nominations)| {
|
||||
let nominator_votes = <Nominators<T>>::iter().map(|(nominator, nominations)| {
|
||||
let Nominations { submitted_in, mut targets, suppressed: _ } = nominations;
|
||||
|
||||
// Filter out nomination targets which were nominated before the most recent
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// Deprecated storages used for migration from v1.0.0 to v2.0.0 only.
|
||||
|
||||
use crate::{Trait, BalanceOf, MomentOf, SessionIndex, Exposure, UnlockChunk};
|
||||
use codec::{Encode, Decode, HasCompact};
|
||||
use frame_support::{decl_module, decl_storage};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// Reward points of an era. Used to split era total payout between validators.
|
||||
#[derive(Encode, Decode, Default)]
|
||||
pub struct EraPoints {
|
||||
/// Total number of points. Equals the sum of reward points for each validator.
|
||||
pub total: u32,
|
||||
/// The reward points earned by a given validator. The index of this vec corresponds to the
|
||||
/// index into the current validator set.
|
||||
pub individual: Vec<u32>,
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct OldStakingLedger<AccountId, Balance: HasCompact> {
|
||||
pub stash: AccountId,
|
||||
#[codec(compact)]
|
||||
pub total: Balance,
|
||||
#[codec(compact)]
|
||||
pub active: Balance,
|
||||
pub unlocking: Vec<UnlockChunk<Balance>>,
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin { }
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
pub trait Store for Module<T: Trait> as Staking {
|
||||
pub SlotStake: BalanceOf<T>;
|
||||
|
||||
/// The currently elected validator set keyed by stash account ID.
|
||||
pub CurrentElected: Vec<T::AccountId>;
|
||||
|
||||
/// The start of the current era.
|
||||
pub CurrentEraStart: MomentOf<T>;
|
||||
|
||||
/// The session index at which the current era started.
|
||||
pub CurrentEraStartSessionIndex: SessionIndex;
|
||||
|
||||
/// Rewards for the current era. Using indices of current elected set.
|
||||
pub CurrentEraPointsEarned: EraPoints;
|
||||
|
||||
/// Nominators for a particular account that is in action right now. You can't iterate
|
||||
/// through validators here, but you can find them in the Session module.
|
||||
///
|
||||
/// This is keyed by the stash account.
|
||||
pub Stakers: map hasher(blake2_256) T::AccountId => Exposure<T::AccountId, BalanceOf<T>>;
|
||||
|
||||
/// Old upgrade flag.
|
||||
pub IsUpgraded: bool;
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Update storage from v1.0.0 to v2.0.0
|
||||
//!
|
||||
//! In old version the staking module has several issue about handling session delay, the
|
||||
//! current era was always considered the active one.
|
||||
//!
|
||||
//! After the migration the current era will still be considered the active one for the era of
|
||||
//! the upgrade. And the delay issue will be fixed when planning the next era.
|
||||
// * create:
|
||||
// * ActiveEraStart
|
||||
// * ErasRewardPoints
|
||||
// * ActiveEra
|
||||
// * ErasStakers
|
||||
// * ErasStakersClipped
|
||||
// * ErasValidatorPrefs
|
||||
// * ErasTotalStake
|
||||
// * ErasStartSessionIndex
|
||||
// * translate StakingLedger
|
||||
// * removal of:
|
||||
// * Stakers
|
||||
// * SlotStake
|
||||
// * CurrentElected
|
||||
// * CurrentEraStart
|
||||
// * CurrentEraStartSessionIndex
|
||||
// * CurrentEraPointsEarned
|
||||
|
||||
use super::*;
|
||||
mod deprecated;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
#[cfg(test)]
|
||||
mod test_upgrade_from_master_dataset;
|
||||
|
||||
pub fn on_runtime_upgrade<T: Trait>() {
|
||||
match StorageVersion::get() {
|
||||
Releases::V2_0_0 => return,
|
||||
Releases::V1_0_0 => upgrade_v1_to_v2::<T>(),
|
||||
}
|
||||
}
|
||||
|
||||
fn upgrade_v1_to_v2<T: Trait>() {
|
||||
deprecated::IsUpgraded::kill();
|
||||
|
||||
let current_era_start_index = deprecated::CurrentEraStartSessionIndex::get();
|
||||
let current_era = <Module<T> as Store>::CurrentEra::get().unwrap_or(0);
|
||||
let current_era_start = deprecated::CurrentEraStart::<T>::get();
|
||||
<Module<T> as Store>::ErasStartSessionIndex::insert(current_era, current_era_start_index);
|
||||
<Module<T> as Store>::ActiveEra::put(ActiveEraInfo {
|
||||
index: current_era,
|
||||
start: Some(current_era_start),
|
||||
});
|
||||
|
||||
let current_elected = deprecated::CurrentElected::<T>::get();
|
||||
let mut current_total_stake = <BalanceOf<T>>::zero();
|
||||
for validator in ¤t_elected {
|
||||
let exposure = deprecated::Stakers::<T>::get(validator);
|
||||
current_total_stake += exposure.total;
|
||||
<Module<T> as Store>::ErasStakers::insert(current_era, validator, &exposure);
|
||||
|
||||
let mut exposure_clipped = exposure;
|
||||
let clipped_max_len = T::MaxNominatorRewardedPerValidator::get() as usize;
|
||||
if exposure_clipped.others.len() > clipped_max_len {
|
||||
exposure_clipped.others.sort_unstable_by(|a, b| a.value.cmp(&b.value).reverse());
|
||||
exposure_clipped.others.truncate(clipped_max_len);
|
||||
}
|
||||
<Module<T> as Store>::ErasStakersClipped::insert(current_era, validator, exposure_clipped);
|
||||
|
||||
let pref = <Module<T> as Store>::Validators::get(validator);
|
||||
<Module<T> as Store>::ErasValidatorPrefs::insert(current_era, validator, pref);
|
||||
}
|
||||
<Module<T> as Store>::ErasTotalStake::insert(current_era, current_total_stake);
|
||||
|
||||
let points = deprecated::CurrentEraPointsEarned::get();
|
||||
<Module<T> as Store>::ErasRewardPoints::insert(current_era, EraRewardPoints {
|
||||
total: points.total,
|
||||
individual: current_elected.iter().cloned().zip(points.individual.iter().cloned()).collect(),
|
||||
});
|
||||
|
||||
let res = <Module<T> as Store>::Ledger::translate_values(
|
||||
|old: deprecated::OldStakingLedger<T::AccountId, BalanceOf<T>>| StakingLedger {
|
||||
stash: old.stash,
|
||||
total: old.total,
|
||||
active: old.active,
|
||||
unlocking: old.unlocking,
|
||||
last_reward: None,
|
||||
}
|
||||
);
|
||||
if let Err(e) = res {
|
||||
frame_support::print("Encountered error in migration of Staking::Ledger map.");
|
||||
frame_support::print("The number of removed key/value is:");
|
||||
frame_support::print(e);
|
||||
}
|
||||
|
||||
|
||||
// Kill old storages
|
||||
deprecated::Stakers::<T>::remove_all();
|
||||
deprecated::SlotStake::<T>::kill();
|
||||
deprecated::CurrentElected::<T>::kill();
|
||||
deprecated::CurrentEraStart::<T>::kill();
|
||||
deprecated::CurrentEraStartSessionIndex::kill();
|
||||
deprecated::CurrentEraPointsEarned::kill();
|
||||
|
||||
StorageVersion::put(Releases::V2_0_0);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,220 +0,0 @@
|
||||
use crate::*;
|
||||
use crate::mock::*;
|
||||
use frame_support::storage::migration::*;
|
||||
use sp_core::hashing::blake2_256;
|
||||
use super::test_upgrade_from_master_dataset;
|
||||
use sp_runtime::traits::OnRuntimeUpgrade;
|
||||
|
||||
#[test]
|
||||
fn upgrade_works() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
start_era(3);
|
||||
|
||||
assert_eq!(Session::validators(), vec![21, 11]);
|
||||
|
||||
// Insert fake data to check the migration
|
||||
put_storage_value::<Vec<AccountId>>(b"Staking", b"CurrentElected", b"", vec![21, 31]);
|
||||
put_storage_value::<SessionIndex>(b"Staking", b"CurrentEraStartSessionIndex", b"", 5);
|
||||
put_storage_value::<MomentOf<Test>>(b"Staking", b"CurrentEraStart", b"", 777);
|
||||
put_storage_value(
|
||||
b"Staking", b"Stakers", &blake2_256(&11u64.encode()),
|
||||
Exposure::<AccountId, Balance> {
|
||||
total: 10,
|
||||
own: 10,
|
||||
others: vec![],
|
||||
}
|
||||
);
|
||||
put_storage_value(
|
||||
b"Staking", b"Stakers", &blake2_256(&21u64.encode()),
|
||||
Exposure::<AccountId, Balance> {
|
||||
total: 20,
|
||||
own: 20,
|
||||
others: vec![],
|
||||
}
|
||||
);
|
||||
put_storage_value(
|
||||
b"Staking", b"Stakers", &blake2_256(&31u64.encode()),
|
||||
Exposure::<AccountId, Balance> {
|
||||
total: 30,
|
||||
own: 30,
|
||||
others: vec![],
|
||||
}
|
||||
);
|
||||
put_storage_value::<(u32, Vec<u32>)>(b"Staking", b"CurrentEraPointsEarned", b"", (12, vec![2, 10]));
|
||||
<Staking as Store>::ErasStakers::remove_all();
|
||||
<Staking as Store>::ErasStakersClipped::remove_all();
|
||||
|
||||
<Staking as Store>::StorageVersion::put(Releases::V1_0_0);
|
||||
|
||||
// Perform upgrade
|
||||
Staking::on_runtime_upgrade();
|
||||
|
||||
assert_eq!(<Staking as Store>::StorageVersion::get(), Releases::V2_0_0);
|
||||
|
||||
// Check migration
|
||||
assert_eq!(<Staking as Store>::ErasStartSessionIndex::get(3).unwrap(), 5);
|
||||
assert_eq!(<Staking as Store>::ErasRewardPoints::get(3), EraRewardPoints {
|
||||
total: 12,
|
||||
individual: vec![(21, 2), (31, 10)].into_iter().collect(),
|
||||
});
|
||||
assert_eq!(<Staking as Store>::ActiveEra::get().unwrap().index, 3);
|
||||
assert_eq!(<Staking as Store>::ActiveEra::get().unwrap().start, Some(777));
|
||||
assert_eq!(<Staking as Store>::CurrentEra::get().unwrap(), 3);
|
||||
assert_eq!(<Staking as Store>::ErasStakers::get(3, 11), Exposure {
|
||||
total: 0,
|
||||
own: 0,
|
||||
others: vec![],
|
||||
});
|
||||
assert_eq!(<Staking as Store>::ErasStakers::get(3, 21), Exposure {
|
||||
total: 20,
|
||||
own: 20,
|
||||
others: vec![],
|
||||
});
|
||||
assert_eq!(<Staking as Store>::ErasStakers::get(3, 31), Exposure {
|
||||
total: 30,
|
||||
own: 30,
|
||||
others: vec![],
|
||||
});
|
||||
assert_eq!(<Staking as Store>::ErasStakersClipped::get(3, 11), Exposure {
|
||||
total: 0,
|
||||
own: 0,
|
||||
others: vec![],
|
||||
});
|
||||
assert_eq!(<Staking as Store>::ErasStakersClipped::get(3, 21), Exposure {
|
||||
total: 20,
|
||||
own: 20,
|
||||
others: vec![],
|
||||
});
|
||||
assert_eq!(<Staking as Store>::ErasStakersClipped::get(3, 31), Exposure {
|
||||
total: 30,
|
||||
own: 30,
|
||||
others: vec![],
|
||||
});
|
||||
assert_eq!(<Staking as Store>::ErasValidatorPrefs::get(3, 21), Staking::validators(21));
|
||||
assert_eq!(<Staking as Store>::ErasValidatorPrefs::get(3, 31), Staking::validators(31));
|
||||
assert_eq!(<Staking as Store>::ErasTotalStake::get(3), 50);
|
||||
})
|
||||
}
|
||||
|
||||
// Test that an upgrade from previous test environment works.
|
||||
#[test]
|
||||
fn test_upgrade_from_master_works() {
|
||||
let data_sets = &[
|
||||
test_upgrade_from_master_dataset::_0,
|
||||
test_upgrade_from_master_dataset::_1,
|
||||
test_upgrade_from_master_dataset::_2,
|
||||
test_upgrade_from_master_dataset::_3,
|
||||
test_upgrade_from_master_dataset::_4,
|
||||
test_upgrade_from_master_dataset::_5,
|
||||
test_upgrade_from_master_dataset::_6,
|
||||
test_upgrade_from_master_dataset::_7,
|
||||
test_upgrade_from_master_dataset::_8,
|
||||
];
|
||||
for data_set in data_sets.iter() {
|
||||
let mut storage = sp_runtime::Storage::default();
|
||||
for (key, value) in data_set.iter() {
|
||||
storage.top.insert(key.to_vec(), value.to_vec());
|
||||
}
|
||||
let mut ext = sp_io::TestExternalities::from(storage);
|
||||
ext.execute_with(|| {
|
||||
let old_stakers =
|
||||
get_storage_value::<Vec<AccountId>>(b"Staking", b"CurrentElected", b"").unwrap();
|
||||
let old_staker_0 = old_stakers[0];
|
||||
let old_staker_1 = old_stakers[1];
|
||||
let old_current_era =
|
||||
get_storage_value::<EraIndex>(b"Staking", b"CurrentEra", b"").unwrap();
|
||||
let old_staker_0_exposure = get_storage_value::<Exposure<AccountId, Balance>>(
|
||||
b"Staking", b"Stakers", &blake2_256(&old_staker_0.encode())
|
||||
).unwrap();
|
||||
let old_staker_1_exposure = get_storage_value::<Exposure<AccountId, Balance>>(
|
||||
b"Staking", b"Stakers", &blake2_256(&old_staker_1.encode())
|
||||
).unwrap();
|
||||
let (
|
||||
old_era_points_earned_total,
|
||||
old_era_points_earned_individual
|
||||
) = get_storage_value::<(u32, Vec<u32>)>(b"Staking", b"CurrentEraPointsEarned", b"")
|
||||
.unwrap_or((0, vec![]));
|
||||
|
||||
Staking::on_runtime_upgrade();
|
||||
assert!(<Staking as Store>::StorageVersion::get() == Releases::V2_0_0);
|
||||
|
||||
// Check ActiveEra and CurrentEra
|
||||
let active_era = Staking::active_era().unwrap().index;
|
||||
let current_era = Staking::current_era().unwrap();
|
||||
assert!(current_era == active_era);
|
||||
assert!(current_era == old_current_era);
|
||||
|
||||
// Check ErasStartSessionIndex
|
||||
let active_era_start = Staking::eras_start_session_index(active_era).unwrap();
|
||||
let current_era_start = Staking::eras_start_session_index(current_era).unwrap();
|
||||
let current_session_index = Session::current_index();
|
||||
assert!(current_era_start == active_era_start);
|
||||
assert!(active_era_start <= current_session_index);
|
||||
assert_eq!(<Staking as Store>::ErasStartSessionIndex::iter().count(), 1);
|
||||
|
||||
// Check ErasStakers
|
||||
assert_eq!(<Staking as Store>::ErasStakers::iter().count(), 2);
|
||||
assert_eq!(
|
||||
<Staking as Store>::ErasStakers::get(current_era, old_staker_0),
|
||||
old_staker_0_exposure
|
||||
);
|
||||
assert_eq!(
|
||||
<Staking as Store>::ErasStakers::get(current_era, old_staker_1),
|
||||
old_staker_1_exposure
|
||||
);
|
||||
|
||||
// Check ErasStakersClipped
|
||||
assert_eq!(<Staking as Store>::ErasStakersClipped::iter().count(), 2);
|
||||
assert!(<Staking as Store>::ErasStakersClipped::iter().all(|exposure_clipped| {
|
||||
let max = <Test as Trait>::MaxNominatorRewardedPerValidator::get() as usize;
|
||||
exposure_clipped.others.len() <= max
|
||||
}));
|
||||
assert_eq!(
|
||||
<Staking as Store>::ErasStakersClipped::get(current_era, old_staker_0),
|
||||
old_staker_0_exposure
|
||||
);
|
||||
assert_eq!(
|
||||
<Staking as Store>::ErasStakersClipped::get(current_era, old_staker_1),
|
||||
old_staker_1_exposure
|
||||
);
|
||||
|
||||
// Check ErasValidatorPrefs
|
||||
assert_eq!(<Staking as Store>::ErasValidatorPrefs::iter().count(), 2);
|
||||
assert_eq!(
|
||||
<Staking as Store>::ErasValidatorPrefs::get(current_era, old_staker_0),
|
||||
Staking::validators(old_staker_0)
|
||||
);
|
||||
assert_eq!(
|
||||
<Staking as Store>::ErasValidatorPrefs::get(current_era, old_staker_1),
|
||||
Staking::validators(old_staker_1)
|
||||
);
|
||||
|
||||
// Check ErasTotalStake
|
||||
assert_eq!(<Staking as Store>::ErasTotalStake::iter().count(), 1);
|
||||
assert_eq!(
|
||||
<Staking as Store>::ErasTotalStake::get(current_era),
|
||||
old_staker_0_exposure.total + old_staker_1_exposure.total
|
||||
);
|
||||
|
||||
// Check ErasRewardPoints
|
||||
assert_eq!(<Staking as Store>::ErasRewardPoints::iter().count(), 1);
|
||||
let mut individual = BTreeMap::new();
|
||||
if let Some(p) = old_era_points_earned_individual.get(0) {
|
||||
individual.insert(old_staker_0, p.clone());
|
||||
}
|
||||
if let Some(p) = old_era_points_earned_individual.get(1) {
|
||||
individual.insert(old_staker_1, p.clone());
|
||||
}
|
||||
assert_eq!(
|
||||
<Staking as Store>::ErasRewardPoints::get(current_era),
|
||||
EraRewardPoints {
|
||||
total: old_era_points_earned_total,
|
||||
individual,
|
||||
}
|
||||
);
|
||||
|
||||
// Check ErasValidatorReward
|
||||
assert_eq!(<Staking as Store>::ErasValidatorReward::iter().count(), 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,8 @@ use sp_staking::{SessionIndex, offence::{OffenceDetails, OnOffenceHandler}};
|
||||
use sp_core::{H256, crypto::key_types};
|
||||
use sp_io;
|
||||
use frame_support::{
|
||||
assert_ok, impl_outer_origin, parameter_types, StorageLinkedMap, StorageValue, StorageMap,
|
||||
StorageDoubleMap,
|
||||
traits::{Currency, Get, FindAuthor},
|
||||
weights::Weight,
|
||||
assert_ok, impl_outer_origin, parameter_types, StorageValue, StorageMap,
|
||||
StorageDoubleMap, IterableStorageMap, traits::{Currency, Get, FindAuthor}, weights::Weight,
|
||||
};
|
||||
use crate::{
|
||||
EraIndex, GenesisConfig, Module, Trait, StakerStatus, ValidatorPrefs, RewardDestination,
|
||||
@@ -140,7 +138,7 @@ impl frame_system::Trait for Test {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
@@ -373,7 +371,7 @@ pub fn check_exposure_all(era: EraIndex) {
|
||||
}
|
||||
|
||||
pub fn check_nominator_all(era: EraIndex) {
|
||||
<Nominators<Test>>::enumerate()
|
||||
<Nominators<Test>>::iter()
|
||||
.for_each(|(acc, _)| check_nominator_exposure(era, acc));
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ fn basic_setup_works() {
|
||||
assert_eq!(Staking::ledger(&1), None);
|
||||
|
||||
// ValidatorPrefs are default
|
||||
assert_eq!(<Validators<Test>>::enumerate().collect::<Vec<_>>(), vec![
|
||||
assert_eq!(<Validators<Test>>::iter().collect::<Vec<_>>(), vec![
|
||||
(31, ValidatorPrefs::default()),
|
||||
(21, ValidatorPrefs::default()),
|
||||
(11, ValidatorPrefs::default())
|
||||
@@ -2769,7 +2769,7 @@ fn slash_kicks_validators_not_nominators() {
|
||||
|
||||
// This is the best way to check that the validator was chilled; `get` will
|
||||
// return default value.
|
||||
for (stash, _) in <Staking as Store>::Validators::enumerate() {
|
||||
for (stash, _) in <Staking as Store>::Validators::iter() {
|
||||
assert!(stash != 11);
|
||||
}
|
||||
|
||||
@@ -2869,7 +2869,7 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() {
|
||||
// Fail: Era not finished yet
|
||||
Error::<Test>::InvalidEraToReward
|
||||
);
|
||||
|
||||
|
||||
// Era 0 can't be rewarded anymore and current era can't be rewarded yet
|
||||
// only era 1 and 2 can be rewarded.
|
||||
|
||||
@@ -2909,7 +2909,7 @@ fn zero_slash_keeps_nominators() {
|
||||
|
||||
// This is the best way to check that the validator was chilled; `get` will
|
||||
// return default value.
|
||||
for (stash, _) in <Staking as Store>::Validators::enumerate() {
|
||||
for (stash, _) in <Staking as Store>::Validators::iter() {
|
||||
assert!(stash != 11);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
codec = { package = "parity-scale-codec", version = "1.2.0", default-features = false, features = ["derive"] }
|
||||
frame-metadata = { version = "11.0.0-alpha.2", default-features = false, path = "../metadata" }
|
||||
sp-std = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/std" }
|
||||
sp-io ={ path = "../../primitives/io", default-features = false , version = "2.0.0-alpha.2"}
|
||||
sp-io = { path = "../../primitives/io", default-features = false , version = "2.0.0-alpha.2"}
|
||||
sp-runtime = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-core = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/core" }
|
||||
sp-arithmetic = { version = "2.0.0-alpha.2", default-features = false, path = "../../primitives/arithmetic" }
|
||||
|
||||
@@ -33,8 +33,8 @@ use proc_macro::TokenStream;
|
||||
/// decl_storage! {
|
||||
/// trait Store for Module<T: Trait> as Example {
|
||||
/// Foo get(fn foo) config(): u32=12;
|
||||
/// Bar: map hasher(blake2_256) u32 => u32;
|
||||
/// pub Zed build(|config| vec![(0, 0)]): linked_map hasher(blake2_256) u32 => u32;
|
||||
/// Bar: map hasher(identity) u32 => u32;
|
||||
/// pub Zed build(|config| vec![(0, 0)]): map hasher(identity) u32 => u32;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
@@ -70,10 +70,28 @@ use proc_macro::TokenStream;
|
||||
/// And [`StoragePrefixedMap`](../frame_support/storage/trait.StoragePrefixedMap.html).
|
||||
///
|
||||
/// `$hash` representing a choice of hashing algorithms available in the
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait.
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait. You will generally want to use one
|
||||
/// of three hashers:
|
||||
/// * `blake2_128_concat`: The default, safe choice. Use if you are unsure or don't care. It is
|
||||
/// secure against user-tainted keys, fairly fast and memory-efficient and supports
|
||||
/// iteration over its keys and values. This must be used if the keys of your map can be
|
||||
/// selected *en masse* by untrusted users.
|
||||
/// * `twox_64_concat`: This is an insecure hasher and can only be used safely if you know that
|
||||
/// the preimages cannot be chosen at will by untrusted users. It is memory-efficient, extremely
|
||||
/// performant and supports iteration over its keys and values. You can safely use this is the
|
||||
/// key is:
|
||||
/// - A (slowly) incrementing index.
|
||||
/// - Known to be the result of a cryptographic hash (though `identity` is a better choice here).
|
||||
/// - Known to be the public key of a cryptographic key pair in existence.
|
||||
/// * `identity`: This is not a hasher at all, and just uses the key material directly. Since it
|
||||
/// does no hashing or appending, it's the fastest possible hasher, however, it's also the least
|
||||
/// secure. It can be used only if you know that the key will be cryptographically/securely
|
||||
/// randomly distributed over the binary encoding space. In most cases this will not be true.
|
||||
/// One case where it is true, however, if where the key is itself the result of a cryptographic
|
||||
/// hash of some existent data.
|
||||
///
|
||||
/// `blake2_256` and `blake2_128_concat` are strong hasher. One should use another hasher
|
||||
/// with care, see generator documentation.
|
||||
/// Other hashers will tend to be "opaque" and not support iteration over the keys in the
|
||||
/// map. It is not recommended to use these.
|
||||
///
|
||||
/// The generator is implemented with:
|
||||
/// * `module_prefix`: $module_prefix
|
||||
@@ -85,36 +103,6 @@ use proc_macro::TokenStream;
|
||||
/// twox128(module_prefix) ++ twox128(storage_prefix) ++ hasher(encode(key))
|
||||
/// ```
|
||||
///
|
||||
/// * Linked map: `Foo: linked_map hasher($hash) type => type`: Implements the
|
||||
/// [`StorageLinkedMap`](../frame_support/storage/trait.StorageLinkedMap.html) trait using the
|
||||
/// [`StorageLinkedMap generator`](../frame_support/storage/generator/trait.StorageLinkedMap.html).
|
||||
/// And [`StoragePrefixedMap`](../frame_support/storage/trait.StoragePrefixedMap.html).
|
||||
///
|
||||
/// `$hash` representing a choice of hashing algorithms available in the
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait.
|
||||
///
|
||||
/// `blake2_256` and `blake2_128_concat` are strong hasher. One should use another hasher
|
||||
/// with care, see generator documentation.
|
||||
///
|
||||
/// All key formatting logic can be accessed in a type-agnostic format via the
|
||||
/// `KeyFormat` trait, which
|
||||
/// is implemented for the storage linked map type as well.
|
||||
///
|
||||
/// The generator key format is implemented with:
|
||||
/// * `module_prefix`: $module_prefix
|
||||
/// * `storage_prefix`: storage_name
|
||||
/// * `head_prefix`: `"HeadOf" ++ storage_name`
|
||||
/// * `Hasher`: $hash
|
||||
///
|
||||
/// Thus the keys are stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(module_prefix) ++ Twox128(storage_prefix) ++ Hasher(encode(key))
|
||||
/// ```
|
||||
/// and head is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(module_prefix) ++ Twox128(head_prefix)
|
||||
/// ```
|
||||
///
|
||||
/// * Double map: `Foo: double_map hasher($hash1) u32, hasher($hash2) u32 => u32`: Implements the
|
||||
/// [`StorageDoubleMap`](../frame_support/storage/trait.StorageDoubleMap.html) trait using the
|
||||
/// [`StorageDoubleMap generator`](../frame_support/storage/generator/trait.StorageDoubleMap.html).
|
||||
@@ -124,14 +112,6 @@ use proc_macro::TokenStream;
|
||||
/// [`Hashable`](../frame_support/trait.Hashable.html) trait. They must be chosen with care, see
|
||||
/// generator documentation.
|
||||
///
|
||||
/// If the first key is untrusted, a cryptographic `hasher` such as `blake2_256` or
|
||||
/// `blake2_128_concat` must be used.
|
||||
/// Otherwise, other values of all storage items can be compromised.
|
||||
///
|
||||
/// If the second key is untrusted, a cryptographic `hasher` such as `blake2_256` or
|
||||
/// `blake2_128_concat` must be used.
|
||||
/// Otherwise, other items in storage with the same first key can be compromised.
|
||||
///
|
||||
/// The generator is implemented with:
|
||||
/// * `module_prefix`: $module_prefix
|
||||
/// * `storage_prefix`: storage_name
|
||||
@@ -145,10 +125,16 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// Supported hashers (ordered from least to best security):
|
||||
///
|
||||
/// * `twox_64_concat` - TwoX with 64bit + key concatenated.
|
||||
/// * `identity` - Just the unrefined key material. Use only when it is known to be a secure hash
|
||||
/// already. The most efficient and iterable over keys.
|
||||
/// * `twox_64_concat` - TwoX with 64bit + key concatenated. Use only when an untrusted source
|
||||
/// cannot select and insert key values. Very efficient and iterable over keys.
|
||||
/// * `blake2_128_concat` - Blake2 with 128bit + key concatenated. Slower but safe to use in all
|
||||
/// circumstances. Iterable over keys.
|
||||
///
|
||||
/// Deprecated hashers, which do not support iteration over keys include:
|
||||
/// * `twox_128` - TwoX with 128bit.
|
||||
/// * `twox_256` - TwoX with with 256bit.
|
||||
/// * `blake2_128_concat` - Blake2 with 128bit + key concatenated.
|
||||
/// * `blake2_128` - Blake2 with 128bit.
|
||||
/// * `blake2_256` - Blake2 with 256bit.
|
||||
///
|
||||
|
||||
@@ -88,7 +88,7 @@ impl BuilderDef {
|
||||
}}
|
||||
},
|
||||
StorageLineTypeDef::Simple(_) => unreachable!(),
|
||||
StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => {
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
let key = &map.key;
|
||||
quote!{{
|
||||
#data
|
||||
|
||||
@@ -93,7 +93,7 @@ impl GenesisConfigDef {
|
||||
|
||||
let typ = match &line.storage_type {
|
||||
StorageLineTypeDef::Simple(_) => (*value_type).clone(),
|
||||
StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => {
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
let key = &map.key;
|
||||
parse_quote!( Vec<(#key, #value_type)> )
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn impl_getters(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStrea
|
||||
}
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => {
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
let key = &map.key;
|
||||
let value = &map.value;
|
||||
quote!{
|
||||
|
||||
@@ -41,20 +41,7 @@ fn storage_line_metadata_type(scrate: &TokenStream, line: &StorageLineDefExt) ->
|
||||
hasher: #scrate::metadata::#hasher,
|
||||
key: #scrate::metadata::DecodeDifferent::Encode(#key),
|
||||
value: #scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
is_linked: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
let hasher = map.hasher.into_metadata();
|
||||
let key = &map.key;
|
||||
let key = clean_type_string("e!(#key).to_string());
|
||||
quote!{
|
||||
#scrate::metadata::StorageEntryType::Map {
|
||||
hasher: #scrate::metadata::#hasher,
|
||||
key: #scrate::metadata::DecodeDifferent::Encode(#key),
|
||||
value: #scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
is_linked: true,
|
||||
unused: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -235,10 +235,6 @@ impl StorageLineDefExt {
|
||||
ext::type_contains_ident(&map.key, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.value, &def.module_runtime_generic)
|
||||
}
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
ext::type_contains_ident(&map.key, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.value, &def.module_runtime_generic)
|
||||
}
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
ext::type_contains_ident(&map.key1, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.key2, &def.module_runtime_generic)
|
||||
@@ -249,7 +245,6 @@ impl StorageLineDefExt {
|
||||
let query_type = match &storage_def.storage_type {
|
||||
StorageLineTypeDef::Simple(value) => value.clone(),
|
||||
StorageLineTypeDef::Map(map) => map.value.clone(),
|
||||
StorageLineTypeDef::LinkedMap(map) => map.value.clone(),
|
||||
StorageLineTypeDef::DoubleMap(map) => map.value.clone(),
|
||||
};
|
||||
let is_option = ext::extract_type_option(&query_type).is_some();
|
||||
@@ -291,10 +286,6 @@ impl StorageLineDefExt {
|
||||
let key = &map.key;
|
||||
quote!( StorageMap<#key, #value_type> )
|
||||
},
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
let key = &map.key;
|
||||
quote!( StorageLinkedMap<#key, #value_type> )
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let key1 = &map.key1;
|
||||
let key2 = &map.key2;
|
||||
@@ -336,7 +327,6 @@ impl StorageLineDefExt {
|
||||
|
||||
pub enum StorageLineTypeDef {
|
||||
Map(MapDef),
|
||||
LinkedMap(MapDef),
|
||||
DoubleMap(DoubleMapDef),
|
||||
Simple(syn::Type),
|
||||
}
|
||||
@@ -372,6 +362,7 @@ pub enum HasherKind {
|
||||
Twox256,
|
||||
Twox128,
|
||||
Twox64Concat,
|
||||
Identity,
|
||||
}
|
||||
|
||||
impl HasherKind {
|
||||
@@ -383,6 +374,7 @@ impl HasherKind {
|
||||
HasherKind::Twox256 => quote!( Twox256 ),
|
||||
HasherKind::Twox128 => quote!( Twox128 ),
|
||||
HasherKind::Twox64Concat => quote!( Twox64Concat ),
|
||||
HasherKind::Identity => quote!( Identity ),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,6 +386,7 @@ impl HasherKind {
|
||||
HasherKind::Twox256 => quote!( StorageHasher::Twox256 ),
|
||||
HasherKind::Twox128 => quote!( StorageHasher::Twox128 ),
|
||||
HasherKind::Twox64Concat => quote!( StorageHasher::Twox64Concat ),
|
||||
HasherKind::Identity => quote!( StorageHasher::Identity ),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -420,7 +413,6 @@ pub fn decl_storage_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStr
|
||||
use #scrate::{
|
||||
StorageValue as _,
|
||||
StorageMap as _,
|
||||
StorageLinkedMap as _,
|
||||
StorageDoubleMap as _,
|
||||
StoragePrefixedMap as _,
|
||||
};
|
||||
|
||||
@@ -27,15 +27,18 @@ mod keyword {
|
||||
syn::custom_keyword!(build);
|
||||
syn::custom_keyword!(get);
|
||||
syn::custom_keyword!(map);
|
||||
syn::custom_keyword!(linked_map);
|
||||
syn::custom_keyword!(double_map);
|
||||
syn::custom_keyword!(blake2_256);
|
||||
syn::custom_keyword!(blake2_128);
|
||||
syn::custom_keyword!(opaque_blake2_256);
|
||||
syn::custom_keyword!(opaque_blake2_128);
|
||||
syn::custom_keyword!(blake2_128_concat);
|
||||
syn::custom_keyword!(twox_256);
|
||||
syn::custom_keyword!(twox_128);
|
||||
syn::custom_keyword!(opaque_twox_256);
|
||||
syn::custom_keyword!(opaque_twox_128);
|
||||
syn::custom_keyword!(twox_64_concat);
|
||||
syn::custom_keyword!(identity);
|
||||
syn::custom_keyword!(hasher);
|
||||
syn::custom_keyword!(tainted);
|
||||
syn::custom_keyword!(natural);
|
||||
syn::custom_keyword!(prehashed);
|
||||
}
|
||||
|
||||
/// Specific `Opt` to implement structure with optional parsing
|
||||
@@ -194,7 +197,6 @@ impl_parse_for_opt!(DeclStorageBuild => keyword::build);
|
||||
#[derive(ToTokens, Debug)]
|
||||
enum DeclStorageType {
|
||||
Map(DeclStorageMap),
|
||||
LinkedMap(DeclStorageLinkedMap),
|
||||
DoubleMap(DeclStorageDoubleMap),
|
||||
Simple(syn::Type),
|
||||
}
|
||||
@@ -203,8 +205,6 @@ impl syn::parse::Parse for DeclStorageType {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
|
||||
if input.peek(keyword::map) {
|
||||
Ok(Self::Map(input.parse()?))
|
||||
} else if input.peek(keyword::linked_map) {
|
||||
Ok(Self::LinkedMap(input.parse()?))
|
||||
} else if input.peek(keyword::double_map) {
|
||||
Ok(Self::DoubleMap(input.parse()?))
|
||||
} else {
|
||||
@@ -222,15 +222,6 @@ struct DeclStorageMap {
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageLinkedMap {
|
||||
pub map_keyword: keyword::linked_map,
|
||||
pub hasher: Opt<SetHasher>,
|
||||
pub key: syn::Type,
|
||||
pub ass_keyword: Token![=>],
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageDoubleMap {
|
||||
pub map_keyword: keyword::double_map,
|
||||
@@ -245,29 +236,38 @@ struct DeclStorageDoubleMap {
|
||||
|
||||
#[derive(ToTokens, Debug)]
|
||||
enum Hasher {
|
||||
Blake2_256(keyword::blake2_256),
|
||||
Blake2_128(keyword::blake2_128),
|
||||
Blake2_256(keyword::opaque_blake2_256),
|
||||
Blake2_128(keyword::opaque_blake2_128),
|
||||
Blake2_128Concat(keyword::blake2_128_concat),
|
||||
Twox256(keyword::twox_256),
|
||||
Twox128(keyword::twox_128),
|
||||
Twox256(keyword::opaque_twox_256),
|
||||
Twox128(keyword::opaque_twox_128),
|
||||
Twox64Concat(keyword::twox_64_concat),
|
||||
Identity(keyword::identity),
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for Hasher {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(keyword::blake2_256) {
|
||||
if lookahead.peek(keyword::opaque_blake2_256) {
|
||||
Ok(Self::Blake2_256(input.parse()?))
|
||||
} else if lookahead.peek(keyword::blake2_128) {
|
||||
} else if lookahead.peek(keyword::opaque_blake2_128) {
|
||||
Ok(Self::Blake2_128(input.parse()?))
|
||||
} else if lookahead.peek(keyword::blake2_128_concat) {
|
||||
Ok(Self::Blake2_128Concat(input.parse()?))
|
||||
} else if lookahead.peek(keyword::twox_256) {
|
||||
} else if lookahead.peek(keyword::opaque_twox_256) {
|
||||
Ok(Self::Twox256(input.parse()?))
|
||||
} else if lookahead.peek(keyword::twox_128) {
|
||||
} else if lookahead.peek(keyword::opaque_twox_128) {
|
||||
Ok(Self::Twox128(input.parse()?))
|
||||
} else if lookahead.peek(keyword::twox_64_concat) {
|
||||
Ok(Self::Twox64Concat(input.parse()?))
|
||||
} else if lookahead.peek(keyword::identity) {
|
||||
Ok(Self::Identity(input.parse()?))
|
||||
} else if lookahead.peek(keyword::tainted) {
|
||||
Ok(Self::Blake2_128Concat(input.parse()?))
|
||||
} else if lookahead.peek(keyword::natural) {
|
||||
Ok(Self::Twox64Concat(input.parse()?))
|
||||
} else if lookahead.peek(keyword::prehashed) {
|
||||
Ok(Self::Identity(input.parse()?))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
@@ -313,6 +313,7 @@ impl From<Hasher> for super::HasherKind {
|
||||
Hasher::Twox256(_) => super::HasherKind::Twox256,
|
||||
Hasher::Twox128(_) => super::HasherKind::Twox128,
|
||||
Hasher::Twox64Concat(_) => super::HasherKind::Twox64Concat,
|
||||
Hasher::Identity(_) => super::HasherKind::Identity,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -464,7 +465,7 @@ fn parse_storage_line_defs(
|
||||
let span = line.storage_type.span();
|
||||
let no_hasher_error = || syn::Error::new(
|
||||
span,
|
||||
"Default hasher has been removed, use explicit hasher(blake2_256) instead."
|
||||
"Default hasher has been removed, use explicit hasher(blake2_128_concat) instead."
|
||||
);
|
||||
|
||||
let storage_type = match line.storage_type {
|
||||
@@ -475,13 +476,6 @@ fn parse_storage_line_defs(
|
||||
value: map.value,
|
||||
}
|
||||
),
|
||||
DeclStorageType::LinkedMap(map) => super::StorageLineTypeDef::LinkedMap(
|
||||
super::MapDef {
|
||||
hasher: map.hasher.inner.ok_or_else(no_hasher_error)?.into(),
|
||||
key: map.key,
|
||||
value: map.value,
|
||||
}
|
||||
),
|
||||
DeclStorageType::DoubleMap(map) => super::StorageLineTypeDef::DoubleMap(
|
||||
super::DoubleMapDef {
|
||||
hasher1: map.hasher1.inner.ok_or_else(no_hasher_error)?.into(),
|
||||
|
||||
@@ -158,47 +158,6 @@ pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStre
|
||||
}
|
||||
)
|
||||
},
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
let hasher = map.hasher.to_storage_hasher_struct();
|
||||
|
||||
let head_prefix_str = syn::LitStr::new(
|
||||
&format!("HeadOf{}", line.name.to_string()),
|
||||
line.name.span(),
|
||||
);
|
||||
|
||||
quote!(
|
||||
impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct
|
||||
#optional_storage_where_clause
|
||||
{
|
||||
type Query = #query_type;
|
||||
type KeyFormat = Self;
|
||||
|
||||
fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query {
|
||||
#from_optional_value_to_query
|
||||
}
|
||||
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> {
|
||||
#from_query_to_optional_value
|
||||
}
|
||||
}
|
||||
|
||||
impl<#impl_trait> #scrate::storage::generator::LinkedMapKeyFormat for #storage_struct {
|
||||
type Hasher = #scrate::#hasher;
|
||||
|
||||
fn module_prefix() -> &'static [u8] {
|
||||
#instance_or_inherent::PREFIX.as_bytes()
|
||||
}
|
||||
|
||||
fn storage_prefix() -> &'static [u8] {
|
||||
#storage_name_str.as_bytes()
|
||||
}
|
||||
|
||||
fn head_prefix() -> &'static [u8] {
|
||||
#head_prefix_str.as_bytes()
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let hasher1 = map.hasher1.to_storage_hasher_struct();
|
||||
let hasher2 = map.hasher2.to_storage_hasher_struct();
|
||||
|
||||
@@ -28,6 +28,7 @@ pub trait Hashable: Sized {
|
||||
fn twox_128(&self) -> [u8; 16];
|
||||
fn twox_256(&self) -> [u8; 32];
|
||||
fn twox_64_concat(&self) -> Vec<u8>;
|
||||
fn identity(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
impl<T: Codec> Hashable for T {
|
||||
@@ -49,6 +50,7 @@ impl<T: Codec> Hashable for T {
|
||||
fn twox_64_concat(&self) -> Vec<u8> {
|
||||
self.using_encoded(Twox64Concat::hash)
|
||||
}
|
||||
fn identity(&self) -> Vec<u8> { self.encode() }
|
||||
}
|
||||
|
||||
/// Hasher to use to hash keys to insert to storage.
|
||||
@@ -57,6 +59,25 @@ pub trait StorageHasher: 'static {
|
||||
fn hash(x: &[u8]) -> Self::Output;
|
||||
}
|
||||
|
||||
/// Hasher to use to hash keys to insert to storage.
|
||||
pub trait ReversibleStorageHasher: StorageHasher {
|
||||
fn reverse(x: &[u8]) -> &[u8];
|
||||
}
|
||||
|
||||
/// Store the key directly.
|
||||
pub struct Identity;
|
||||
impl StorageHasher for Identity {
|
||||
type Output = Vec<u8>;
|
||||
fn hash(x: &[u8]) -> Vec<u8> {
|
||||
x.to_vec()
|
||||
}
|
||||
}
|
||||
impl ReversibleStorageHasher for Identity {
|
||||
fn reverse(x: &[u8]) -> &[u8] {
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash storage keys with `concat(twox64(key), key)`
|
||||
pub struct Twox64Concat;
|
||||
impl StorageHasher for Twox64Concat {
|
||||
@@ -69,6 +90,11 @@ impl StorageHasher for Twox64Concat {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
impl ReversibleStorageHasher for Twox64Concat {
|
||||
fn reverse(x: &[u8]) -> &[u8] {
|
||||
&x[8..]
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash storage keys with `concat(blake2_128(key), key)`
|
||||
pub struct Blake2_128Concat;
|
||||
@@ -82,6 +108,11 @@ impl StorageHasher for Blake2_128Concat {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
impl ReversibleStorageHasher for Blake2_128Concat {
|
||||
fn reverse(x: &[u8]) -> &[u8] {
|
||||
&x[16..]
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash storage keys with blake2 128
|
||||
pub struct Blake2_128;
|
||||
|
||||
@@ -67,11 +67,12 @@ pub mod traits;
|
||||
pub mod weights;
|
||||
|
||||
pub use self::hash::{
|
||||
Twox256, Twox128, Blake2_256, Blake2_128, Twox64Concat, Blake2_128Concat, Hashable,
|
||||
Twox256, Twox128, Blake2_256, Blake2_128, Identity, Twox64Concat, Blake2_128Concat, Hashable,
|
||||
StorageHasher
|
||||
};
|
||||
pub use self::storage::{
|
||||
StorageValue, StorageMap, StorageLinkedMap, StorageDoubleMap, StoragePrefixedMap
|
||||
StorageValue, StorageMap, StorageDoubleMap, StoragePrefixedMap, IterableStorageMap,
|
||||
IterableStorageDoubleMap,
|
||||
};
|
||||
pub use self::dispatch::{Parameter, Callable, IsSubType};
|
||||
pub use sp_runtime::{self, ConsensusEngineId, print, traits::Printable};
|
||||
@@ -253,24 +254,24 @@ mod tests {
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Test {
|
||||
pub Data get(fn data) build(|_| vec![(15u32, 42u64)]):
|
||||
linked_map hasher(twox_64_concat) u32 => u64;
|
||||
pub OptionLinkedMap: linked_map hasher(blake2_256) u32 => Option<u32>;
|
||||
map hasher(twox_64_concat) u32 => u64;
|
||||
pub OptionLinkedMap: map hasher(blake2_128_concat) u32 => Option<u32>;
|
||||
pub GenericData get(fn generic_data):
|
||||
linked_map hasher(twox_128) T::BlockNumber => T::BlockNumber;
|
||||
map hasher(identity) T::BlockNumber => T::BlockNumber;
|
||||
pub GenericData2 get(fn generic_data2):
|
||||
linked_map hasher(blake2_256) T::BlockNumber => Option<T::BlockNumber>;
|
||||
map hasher(blake2_128_concat) T::BlockNumber => Option<T::BlockNumber>;
|
||||
pub GetterNoFnKeyword get(no_fn): Option<u32>;
|
||||
|
||||
pub DataDM config(test_config) build(|_| vec![(15u32, 16u32, 42u64)]):
|
||||
double_map hasher(twox_64_concat) u32, hasher(blake2_256) u32 => u64;
|
||||
double_map hasher(twox_64_concat) u32, hasher(blake2_128_concat) u32 => u64;
|
||||
pub GenericDataDM:
|
||||
double_map hasher(blake2_256) T::BlockNumber, hasher(twox_128) T::BlockNumber
|
||||
double_map hasher(blake2_128_concat) T::BlockNumber, hasher(identity) T::BlockNumber
|
||||
=> T::BlockNumber;
|
||||
pub GenericData2DM:
|
||||
double_map hasher(blake2_256) T::BlockNumber, hasher(twox_256) T::BlockNumber
|
||||
double_map hasher(blake2_128_concat) T::BlockNumber, hasher(twox_64_concat) T::BlockNumber
|
||||
=> Option<T::BlockNumber>;
|
||||
pub AppendableDM:
|
||||
double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Vec<u32>;
|
||||
double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) T::BlockNumber => Vec<u32>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,8 +287,16 @@ mod tests {
|
||||
|
||||
type Map = Data;
|
||||
|
||||
trait Sorted { fn sorted(self) -> Self; }
|
||||
impl<T: Ord> Sorted for Vec<T> {
|
||||
fn sorted(mut self) -> Self {
|
||||
self.sort();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_issue_3318() {
|
||||
fn map_issue_3318() {
|
||||
new_test_ext().execute_with(|| {
|
||||
OptionLinkedMap::insert(1, 1);
|
||||
assert_eq!(OptionLinkedMap::get(1), Some(1));
|
||||
@@ -297,31 +306,31 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_swap_works() {
|
||||
fn map_swap_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
OptionLinkedMap::insert(0, 0);
|
||||
OptionLinkedMap::insert(1, 1);
|
||||
OptionLinkedMap::insert(2, 2);
|
||||
OptionLinkedMap::insert(3, 3);
|
||||
|
||||
let collect = || OptionLinkedMap::enumerate().collect::<Vec<_>>();
|
||||
assert_eq!(collect(), vec![(3, 3), (2, 2), (1, 1), (0, 0)]);
|
||||
let collect = || OptionLinkedMap::iter().collect::<Vec<_>>().sorted();
|
||||
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
|
||||
|
||||
// Two existing
|
||||
OptionLinkedMap::swap(1, 2);
|
||||
assert_eq!(collect(), vec![(3, 3), (2, 1), (1, 2), (0, 0)]);
|
||||
assert_eq!(collect(), vec![(0, 0), (1, 2), (2, 1), (3, 3)]);
|
||||
|
||||
// Back to normal
|
||||
OptionLinkedMap::swap(2, 1);
|
||||
assert_eq!(collect(), vec![(3, 3), (2, 2), (1, 1), (0, 0)]);
|
||||
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
|
||||
|
||||
// Left existing
|
||||
OptionLinkedMap::swap(2, 5);
|
||||
assert_eq!(collect(), vec![(5, 2), (3, 3), (1, 1), (0, 0)]);
|
||||
assert_eq!(collect(), vec![(0, 0), (1, 1), (3, 3), (5, 2)]);
|
||||
|
||||
// Right existing
|
||||
OptionLinkedMap::swap(5, 2);
|
||||
assert_eq!(collect(), vec![(2, 2), (3, 3), (1, 1), (0, 0)]);
|
||||
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -356,7 +365,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_basic_insert_remove_should_work() {
|
||||
fn map_basic_insert_remove_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// initialized during genesis
|
||||
assert_eq!(Map::get(&15u32), 42u64);
|
||||
@@ -382,54 +391,45 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_enumeration_and_head_should_work() {
|
||||
fn map_iteration_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Map::head(), Some(15));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(15, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42)]);
|
||||
// insert / remove
|
||||
let key = 17u32;
|
||||
Map::insert(key, 4u64);
|
||||
assert_eq!(Map::head(), Some(key));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 4), (15, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42), (key, 4)]);
|
||||
assert_eq!(Map::take(&15), 42u64);
|
||||
assert_eq!(Map::take(&key), 4u64);
|
||||
assert_eq!(Map::head(), None);
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
|
||||
|
||||
// Add couple of more elements
|
||||
Map::insert(key, 42u64);
|
||||
assert_eq!(Map::head(), Some(key));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42)]);
|
||||
Map::insert(key + 1, 43u64);
|
||||
assert_eq!(Map::head(), Some(key + 1));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key + 1, 43), (key, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42), (key + 1, 43)]);
|
||||
|
||||
// mutate
|
||||
let key = key + 2;
|
||||
Map::mutate(&key, |val| {
|
||||
*val = 15;
|
||||
});
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 15), (key - 1, 43), (key - 2, 42)]);
|
||||
assert_eq!(Map::head(), Some(key));
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43), (key, 15)]);
|
||||
Map::mutate(&key, |val| {
|
||||
*val = 17;
|
||||
});
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key, 17), (key - 1, 43), (key - 2, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43), (key, 17)]);
|
||||
|
||||
// remove first
|
||||
Map::remove(&key);
|
||||
assert_eq!(Map::head(), Some(key - 1));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key - 1, 43), (key - 2, 42)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43)]);
|
||||
|
||||
// remove last from the list
|
||||
Map::remove(&(key - 2));
|
||||
assert_eq!(Map::head(), Some(key - 1));
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![(key - 1, 43)]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 1, 43)]);
|
||||
|
||||
// remove the last element
|
||||
Map::remove(&(key - 1));
|
||||
assert_eq!(Map::head(), None);
|
||||
assert_eq!(Map::enumerate().collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -498,7 +498,7 @@ mod tests {
|
||||
hasher: StorageHasher::Twox64Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
is_linked: true,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructData(PhantomData::<Test>))
|
||||
@@ -509,10 +509,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("OptionLinkedMap"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("u32"),
|
||||
is_linked: true,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructOptionLinkedMap(PhantomData::<Test>))
|
||||
@@ -523,10 +523,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GenericData"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map{
|
||||
hasher: StorageHasher::Twox128,
|
||||
hasher: StorageHasher::Identity,
|
||||
key: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
is_linked: true
|
||||
unused: false
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData(PhantomData::<Test>))
|
||||
@@ -537,10 +537,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GenericData2"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
is_linked: true
|
||||
unused: false
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData2(PhantomData::<Test>))
|
||||
@@ -564,7 +564,7 @@ mod tests {
|
||||
key1: DecodeDifferent::Encode("u32"),
|
||||
key2: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
key2_hasher: StorageHasher::Blake2_256,
|
||||
key2_hasher: StorageHasher::Blake2_128Concat,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructDataDM(PhantomData::<Test>))
|
||||
@@ -575,11 +575,11 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GenericDataDM"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::DoubleMap{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key1: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2_hasher: StorageHasher::Twox128,
|
||||
key2_hasher: StorageHasher::Identity,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericDataDM(PhantomData::<Test>))
|
||||
@@ -590,11 +590,11 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GenericData2DM"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::DoubleMap{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key1: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
key2_hasher: StorageHasher::Twox256,
|
||||
key2_hasher: StorageHasher::Twox64Concat,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::<Test>))
|
||||
@@ -605,11 +605,11 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("AppendableDM"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::DoubleMap{
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key1: DecodeDifferent::Encode("u32"),
|
||||
key2: DecodeDifferent::Encode("T::BlockNumber"),
|
||||
value: DecodeDifferent::Encode("Vec<u32>"),
|
||||
key2_hasher: StorageHasher::Blake2_256,
|
||||
key2_hasher: StorageHasher::Blake2_128Concat,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::<Test>))
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use sp_std::borrow::Borrow;
|
||||
use codec::{Ref, FullCodec, FullEncode, Encode, EncodeLike, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, hash::{StorageHasher, Twox128}, traits::Len};
|
||||
use codec::{Ref, FullCodec, FullEncode, Decode, Encode, EncodeLike, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, traits::Len};
|
||||
use crate::hash::{StorageHasher, Twox128, ReversibleStorageHasher};
|
||||
|
||||
/// Generator for `StorageDoubleMap` used by `decl_storage`.
|
||||
///
|
||||
@@ -55,6 +56,22 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
/// Storage prefix. Used for generating final key.
|
||||
fn storage_prefix() -> &'static [u8];
|
||||
|
||||
/// The full prefix; just the hash of `module_prefix` concatenated to the hash of
|
||||
/// `storage_prefix`.
|
||||
fn prefix_hash() -> Vec<u8> {
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
|
||||
let mut result = Vec::with_capacity(
|
||||
module_prefix_hashed.len() + storage_prefix_hashed.len()
|
||||
);
|
||||
|
||||
result.extend_from_slice(&module_prefix_hashed[..]);
|
||||
result.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
@@ -62,8 +79,7 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the first part of the key used in top storage.
|
||||
fn storage_double_map_final_key1<KArg1>(k1: KArg1) -> Vec<u8>
|
||||
where
|
||||
fn storage_double_map_final_key1<KArg1>(k1: KArg1) -> Vec<u8> where
|
||||
KArg1: EncodeLike<K1>,
|
||||
{
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
@@ -82,19 +98,32 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
}
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_double_map_final_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
fn storage_double_map_final_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8> where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
let mut final_key = Self::storage_double_map_final_key1(k1);
|
||||
final_key.extend_from_slice(k2.using_encoded(Self::Hasher2::hash).as_ref());
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
let key1_hashed = k1.borrow().using_encoded(Self::Hasher1::hash);
|
||||
let key2_hashed = k2.borrow().using_encoded(Self::Hasher2::hash);
|
||||
|
||||
let mut final_key = Vec::with_capacity(
|
||||
module_prefix_hashed.len()
|
||||
+ storage_prefix_hashed.len()
|
||||
+ key1_hashed.as_ref().len()
|
||||
+ key2_hashed.as_ref().len()
|
||||
);
|
||||
|
||||
final_key.extend_from_slice(&module_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(key1_hashed.as_ref());
|
||||
final_key.extend_from_slice(key2_hashed.as_ref());
|
||||
|
||||
final_key
|
||||
}
|
||||
}
|
||||
|
||||
impl<K1, K2, V, G> storage::StorageDoubleMap<K1, K2, V> for G
|
||||
where
|
||||
impl<K1, K2, V, G> storage::StorageDoubleMap<K1, K2, V> for G where
|
||||
K1: FullEncode,
|
||||
K2: FullEncode,
|
||||
V: FullCodec,
|
||||
@@ -102,32 +131,28 @@ where
|
||||
{
|
||||
type Query = G::Query;
|
||||
|
||||
fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8> where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
Self::storage_double_map_final_key(k1, k2)
|
||||
}
|
||||
|
||||
fn contains_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool
|
||||
where
|
||||
fn contains_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
unhashed::exists(&Self::storage_double_map_final_key(k1, k2))
|
||||
}
|
||||
|
||||
fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
G::from_optional_value_to_query(unhashed::get(&Self::storage_double_map_final_key(k1, k2)))
|
||||
}
|
||||
|
||||
fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
@@ -137,8 +162,12 @@ where
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn swap<XKArg1, XKArg2, YKArg1, YKArg2>(x_k1: XKArg1, x_k2: XKArg2, y_k1: YKArg1, y_k2: YKArg2)
|
||||
where
|
||||
fn swap<XKArg1, XKArg2, YKArg1, YKArg2>(
|
||||
x_k1: XKArg1,
|
||||
x_k2: XKArg2,
|
||||
y_k1: YKArg1,
|
||||
y_k2: YKArg2
|
||||
) where
|
||||
XKArg1: EncodeLike<K1>,
|
||||
XKArg2: EncodeLike<K2>,
|
||||
YKArg1: EncodeLike<K1>,
|
||||
@@ -160,8 +189,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg)
|
||||
where
|
||||
fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg) where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
VArg: EncodeLike<V>,
|
||||
@@ -169,8 +197,7 @@ where
|
||||
unhashed::put(&Self::storage_double_map_final_key(k1, k2), &val.borrow())
|
||||
}
|
||||
|
||||
fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2)
|
||||
where
|
||||
fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2) where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
@@ -181,8 +208,8 @@ where
|
||||
unhashed::kill_prefix(Self::storage_double_map_final_key1(k1).as_ref())
|
||||
}
|
||||
|
||||
fn iter_prefix<KArg1>(k1: KArg1) -> storage::PrefixIterator<V>
|
||||
where KArg1: ?Sized + EncodeLike<K1>
|
||||
fn iter_prefix<KArg1>(k1: KArg1) -> storage::PrefixIterator<V> where
|
||||
KArg1: ?Sized + EncodeLike<K1>
|
||||
{
|
||||
let prefix = Self::storage_double_map_final_key1(k1);
|
||||
storage::PrefixIterator::<V> {
|
||||
@@ -192,8 +219,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
|
||||
where
|
||||
fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
F: FnOnce(&mut Self::Query) -> R,
|
||||
@@ -213,8 +239,7 @@ where
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
) -> Result<(), &'static str>
|
||||
where
|
||||
) -> Result<(), &'static str> where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
@@ -246,8 +271,7 @@ where
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
)
|
||||
where
|
||||
) where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
@@ -260,10 +284,10 @@ where
|
||||
.unwrap_or_else(|_| Self::insert(k1, k2, items));
|
||||
}
|
||||
|
||||
fn decode_len<KArg1, KArg2>(key1: KArg1, key2: KArg2) -> Result<usize, &'static str>
|
||||
where KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
V: codec::DecodeLength + Len,
|
||||
fn decode_len<KArg1, KArg2>(key1: KArg1, key2: KArg2) -> Result<usize, &'static str> where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
V: codec::DecodeLength + Len,
|
||||
{
|
||||
let final_key = Self::storage_double_map_final_key(key1, key2);
|
||||
if let Some(v) = unhashed::get_raw(&final_key) {
|
||||
@@ -276,6 +300,135 @@ where
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
fn migrate_keys<
|
||||
OldHasher1: StorageHasher,
|
||||
OldHasher2: StorageHasher,
|
||||
KeyArg1: EncodeLike<K1>,
|
||||
KeyArg2: EncodeLike<K2>,
|
||||
>(key1: KeyArg1, key2: KeyArg2) -> Option<V> {
|
||||
let old_key = {
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
let key1_hashed = key1.borrow().using_encoded(OldHasher1::hash);
|
||||
let key2_hashed = key2.borrow().using_encoded(OldHasher2::hash);
|
||||
|
||||
let mut final_key = Vec::with_capacity(
|
||||
module_prefix_hashed.len()
|
||||
+ storage_prefix_hashed.len()
|
||||
+ key1_hashed.as_ref().len()
|
||||
+ key2_hashed.as_ref().len()
|
||||
);
|
||||
|
||||
final_key.extend_from_slice(&module_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(key1_hashed.as_ref());
|
||||
final_key.extend_from_slice(key2_hashed.as_ref());
|
||||
|
||||
final_key
|
||||
};
|
||||
unhashed::take(old_key.as_ref()).map(|value| {
|
||||
unhashed::put(Self::storage_double_map_final_key(key1, key2).as_ref(), &value);
|
||||
value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility to iterate through items in a storage map.
|
||||
pub struct MapIterator<K, V, Hasher> {
|
||||
prefix: Vec<u8>,
|
||||
previous_key: Vec<u8>,
|
||||
drain: bool,
|
||||
_phantom: ::sp_std::marker::PhantomData<(K, V, Hasher)>,
|
||||
}
|
||||
|
||||
impl<
|
||||
K: Decode + Sized,
|
||||
V: Decode + Sized,
|
||||
Hasher: ReversibleStorageHasher
|
||||
> Iterator for MapIterator<K, V, Hasher> {
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<(K, V)> {
|
||||
loop {
|
||||
let maybe_next = sp_io::storage::next_key(&self.previous_key)
|
||||
.filter(|n| n.starts_with(&self.prefix));
|
||||
break match maybe_next {
|
||||
Some(next) => {
|
||||
self.previous_key = next;
|
||||
match unhashed::get::<V>(&self.previous_key) {
|
||||
Some(value) => {
|
||||
if self.drain {
|
||||
unhashed::kill(&self.previous_key)
|
||||
}
|
||||
let mut key_material = Hasher::reverse(&self.previous_key[self.prefix.len()..]);
|
||||
match K::decode(&mut key_material) {
|
||||
Ok(key) => Some((key, value)),
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
K1: FullCodec,
|
||||
K2: FullCodec,
|
||||
V: FullCodec,
|
||||
G: StorageDoubleMap<K1, K2, V>,
|
||||
> storage::IterableStorageDoubleMap<K1, K2, V> for G where
|
||||
G::Hasher1: ReversibleStorageHasher,
|
||||
G::Hasher2: ReversibleStorageHasher
|
||||
{
|
||||
type Iterator = MapIterator<K2, V, G::Hasher2>;
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn iter(k1: impl EncodeLike<K1>) -> Self::Iterator {
|
||||
let prefix = G::storage_double_map_final_key1(k1);
|
||||
Self::Iterator {
|
||||
prefix: prefix.clone(),
|
||||
previous_key: prefix,
|
||||
drain: false,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn drain(k1: impl EncodeLike<K1>) -> Self::Iterator {
|
||||
let prefix = G::storage_double_map_final_key1(k1);
|
||||
Self::Iterator {
|
||||
prefix: prefix.clone(),
|
||||
previous_key: prefix,
|
||||
drain: true,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn translate<O: Decode, F: Fn(O) -> Option<V>>(f: F) {
|
||||
let prefix = G::prefix_hash();
|
||||
let mut previous_key = prefix.clone();
|
||||
loop {
|
||||
match sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix)) {
|
||||
Some(next) => {
|
||||
previous_key = next;
|
||||
let maybe_value = unhashed::get::<O>(&previous_key);
|
||||
match maybe_value {
|
||||
Some(value) => match f(value) {
|
||||
Some(new) => unhashed::put::<V>(&previous_key, &new),
|
||||
None => unhashed::kill(&previous_key),
|
||||
},
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
None => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,499 +0,0 @@
|
||||
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::{FullCodec, Encode, Decode, EncodeLike, Ref};
|
||||
use crate::{storage::{self, unhashed}, hash::{StorageHasher, Twox128}, traits::Len};
|
||||
use sp_std::{prelude::*, marker::PhantomData};
|
||||
|
||||
/// Generator for `StorageLinkedMap` used by `decl_storage`.
|
||||
///
|
||||
/// By default final key generation rely on `KeyFormat`.
|
||||
pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// The family of key formats used for this map.
|
||||
type KeyFormat: KeyFormat;
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
/// Convert a query to an optional value into storage.
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_linked_map_final_key<KeyArg>(key: KeyArg) -> Vec<u8>
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
{
|
||||
<Self::KeyFormat as KeyFormat>::storage_linked_map_final_key::<KeyArg>(&key)
|
||||
}
|
||||
|
||||
/// Generate the hashed key for head
|
||||
fn storage_linked_map_final_head_key() -> Vec<u8> {
|
||||
<Self::KeyFormat as KeyFormat>::storage_linked_map_final_head_key()
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-abstracted key format used for a family of linked-map types.
|
||||
///
|
||||
/// # Default mapping of keys to a storage path
|
||||
///
|
||||
/// The key for the head of the map is stored at one fixed path:
|
||||
/// ```nocompile
|
||||
/// Twox128(module_prefix) ++ Twox128(head_prefix)
|
||||
/// ```
|
||||
///
|
||||
/// For each key, the value stored under that key is appended with a
|
||||
/// [`Linkage`](struct.Linkage.html) (which hold previous and next key) at the path:
|
||||
/// ```nocompile
|
||||
/// Twox128(module_prefix) ++ Twox128(storage_prefix) ++ Hasher(encode(key))
|
||||
/// ```
|
||||
///
|
||||
/// Enumeration is done by getting the head of the linked map and then iterating getting the
|
||||
/// value and linkage stored at the key until the found linkage has no next key.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_256` must be used. Otherwise, other values in storage can be compromised.
|
||||
pub trait KeyFormat {
|
||||
/// Hasher. Used for generating final key and final head key.
|
||||
type Hasher: StorageHasher;
|
||||
|
||||
/// Module prefix. Used for generating final key.
|
||||
fn module_prefix() -> &'static [u8];
|
||||
|
||||
/// Storage prefix. Used for generating final key.
|
||||
fn storage_prefix() -> &'static [u8];
|
||||
|
||||
/// Storage prefix. Used for generating final head key.
|
||||
fn head_prefix() -> &'static [u8];
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_linked_map_final_key<K>(key: &K) -> Vec<u8>
|
||||
where
|
||||
K: Encode,
|
||||
{
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
let key_hashed = key.using_encoded(Self::Hasher::hash);
|
||||
|
||||
let mut final_key = Vec::with_capacity(
|
||||
module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len()
|
||||
);
|
||||
|
||||
final_key.extend_from_slice(&module_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(key_hashed.as_ref());
|
||||
|
||||
final_key
|
||||
}
|
||||
|
||||
/// Generate the full key used in top storage to store the head of the linked map.
|
||||
fn storage_linked_map_final_head_key() -> Vec<u8> {
|
||||
[
|
||||
Twox128::hash(Self::module_prefix()),
|
||||
Twox128::hash(Self::head_prefix()),
|
||||
].concat()
|
||||
}
|
||||
}
|
||||
|
||||
/// Linkage data of an element (it's successor and predecessor)
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct Linkage<Key> {
|
||||
/// Previous element key in storage (None for the first element)
|
||||
pub previous: Option<Key>,
|
||||
/// Next element key in storage (None for the last element)
|
||||
pub next: Option<Key>,
|
||||
}
|
||||
|
||||
impl<Key> Default for Linkage<Key> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
previous: None,
|
||||
next: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Encode like a linkage.
|
||||
#[derive(Encode)]
|
||||
struct EncodeLikeLinkage<PKey: EncodeLike<Key>, NKey: EncodeLike<Key>, Key: Encode> {
|
||||
// Previous element key in storage (None for the first element)
|
||||
previous: Option<PKey>,
|
||||
// Next element key in storage (None for the last element)
|
||||
next: Option<NKey>,
|
||||
// The key of the linkage this type encode to
|
||||
phantom: core::marker::PhantomData<Key>,
|
||||
}
|
||||
|
||||
/// A key-value pair iterator for enumerable map.
|
||||
pub struct Enumerator<K, V, F> {
|
||||
next: Option<K>,
|
||||
_phantom: PhantomData<(V, F)>,
|
||||
}
|
||||
|
||||
impl<K, V, F> Enumerator<K, V, F> {
|
||||
/// Create an explicit enumerator for testing.
|
||||
#[cfg(test)]
|
||||
pub fn from_head(head: K) -> Self {
|
||||
Enumerator {
|
||||
next: Some(head),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, F> Iterator for Enumerator<K, V, F>
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next = self.next.take()?;
|
||||
|
||||
let (val, linkage): (V, Linkage<K>) = {
|
||||
let next_full_key = F::storage_linked_map_final_key(&next);
|
||||
match read_with_linkage::<K, V>(next_full_key.as_ref()) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map {:?}{:?}: \
|
||||
next value doesn't exist at {:?}",
|
||||
F::module_prefix(), F::storage_prefix(), next_full_key,
|
||||
);
|
||||
return None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.next = linkage.next;
|
||||
Some((next, val))
|
||||
}
|
||||
}
|
||||
|
||||
/// Update linkage when this element is removed.
|
||||
///
|
||||
/// Takes care of updating previous and next elements points
|
||||
/// as well as updates head if the element is first or last.
|
||||
fn remove_linkage<K, V, F>(linkage: Linkage<K>)
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
let next_key = linkage.next.as_ref().map(|k| F::storage_linked_map_final_key(k));
|
||||
let prev_key = linkage.previous.as_ref().map(|k| F::storage_linked_map_final_key(k));
|
||||
|
||||
if let Some(prev_key) = prev_key {
|
||||
// Retrieve previous element and update `next`
|
||||
if let Some(mut res) = read_with_linkage::<K, V>(prev_key.as_ref()) {
|
||||
res.1.next = linkage.next;
|
||||
unhashed::put(prev_key.as_ref(), &res);
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map {:?}{:?}: \
|
||||
previous value doesn't exist at {:?}",
|
||||
F::module_prefix(), F::storage_prefix(), prev_key,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// we were first so let's update the head
|
||||
write_head::<&K, K, F>(linkage.next.as_ref());
|
||||
}
|
||||
if let Some(next_key) = next_key {
|
||||
// Update previous of next element
|
||||
if let Some(mut res) = read_with_linkage::<K, V>(next_key.as_ref()) {
|
||||
res.1.previous = linkage.previous;
|
||||
unhashed::put(next_key.as_ref(), &res);
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map {:?}{:?}: \
|
||||
next value doesn't exist at {:?}",
|
||||
F::module_prefix(), F::storage_prefix(), next_key,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the contained data and its linkage.
|
||||
pub(super) fn read_with_linkage<K, V>(key: &[u8]) -> Option<(V, Linkage<K>)>
|
||||
where
|
||||
K: Decode,
|
||||
V: Decode,
|
||||
{
|
||||
unhashed::get(key)
|
||||
}
|
||||
|
||||
/// Generate linkage for newly inserted element.
|
||||
///
|
||||
/// Takes care of updating head and previous head's pointer.
|
||||
pub(super) fn new_head_linkage<KeyArg, K, V, F>(key: KeyArg) -> Linkage<K>
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
if let Some(head) = read_head::<K, F>() {
|
||||
// update previous head predecessor
|
||||
{
|
||||
let head_key = F::storage_linked_map_final_key(&head);
|
||||
if let Some((data, linkage)) = read_with_linkage::<K, V>(head_key.as_ref()) {
|
||||
let new_linkage = EncodeLikeLinkage::<_, _, K> {
|
||||
previous: Some(Ref::from(&key)),
|
||||
next: linkage.next.as_ref(),
|
||||
phantom: Default::default(),
|
||||
};
|
||||
unhashed::put(head_key.as_ref(), &(data, new_linkage));
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map {:?}{:?}: \
|
||||
head value doesn't exist at {:?}",
|
||||
F::module_prefix(), F::storage_prefix(), head_key,
|
||||
);
|
||||
// Thus we consider we are first - update the head and produce empty linkage
|
||||
|
||||
write_head::<_, _, F>(Some(key));
|
||||
return Linkage::default();
|
||||
}
|
||||
}
|
||||
// update to current head
|
||||
write_head::<_, _, F>(Some(key));
|
||||
// return linkage with pointer to previous head
|
||||
let mut linkage = Linkage::default();
|
||||
linkage.next = Some(head);
|
||||
linkage
|
||||
} else {
|
||||
// we are first - update the head and produce empty linkage
|
||||
write_head::<_, _, F>(Some(key));
|
||||
Linkage::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Read current head pointer.
|
||||
pub(crate) fn read_head<K, F>() -> Option<K>
|
||||
where
|
||||
K: Decode,
|
||||
F: KeyFormat,
|
||||
{
|
||||
unhashed::get(F::storage_linked_map_final_head_key().as_ref())
|
||||
}
|
||||
|
||||
/// Overwrite current head pointer.
|
||||
///
|
||||
/// If `None` is given head is removed from storage.
|
||||
pub(super) fn write_head<KeyArg, K, F>(head: Option<KeyArg>)
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
K: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
match head.as_ref() {
|
||||
Some(head) => unhashed::put(F::storage_linked_map_final_head_key().as_ref(), head),
|
||||
None => unhashed::kill(F::storage_linked_map_final_head_key().as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, G> storage::StorageLinkedMap<K, V> for G
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
G: StorageLinkedMap<K, V>,
|
||||
{
|
||||
type Query = G::Query;
|
||||
|
||||
type Enumerator = Enumerator<K, V, G::KeyFormat>;
|
||||
|
||||
fn contains_key<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool {
|
||||
unhashed::exists(Self::storage_linked_map_final_key(key).as_ref())
|
||||
}
|
||||
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let val = unhashed::get(Self::storage_linked_map_final_key(key).as_ref());
|
||||
G::from_optional_value_to_query(val)
|
||||
}
|
||||
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2) {
|
||||
let final_key1 = Self::storage_linked_map_final_key(Ref::from(&key1));
|
||||
let final_key2 = Self::storage_linked_map_final_key(Ref::from(&key2));
|
||||
let full_value_1 = read_with_linkage::<K, V>(final_key1.as_ref());
|
||||
let full_value_2 = read_with_linkage::<K, V>(final_key2.as_ref());
|
||||
|
||||
match (full_value_1, full_value_2) {
|
||||
// Just keep linkage in order and only swap values.
|
||||
(Some((value1, linkage1)), Some((value2, linkage2))) => {
|
||||
unhashed::put(final_key1.as_ref(), &(value2, linkage1));
|
||||
unhashed::put(final_key2.as_ref(), &(value1, linkage2));
|
||||
}
|
||||
// Remove key and insert the new one.
|
||||
(Some((value, _linkage)), None) => {
|
||||
Self::remove(key1);
|
||||
let linkage = new_head_linkage::<_, _, V, G::KeyFormat>(key2);
|
||||
unhashed::put(final_key2.as_ref(), &(value, linkage));
|
||||
}
|
||||
// Remove key and insert the new one.
|
||||
(None, Some((value, _linkage))) => {
|
||||
Self::remove(key2);
|
||||
let linkage = new_head_linkage::<_, _, V, G::KeyFormat>(key1);
|
||||
unhashed::put(final_key1.as_ref(), &(value, linkage));
|
||||
}
|
||||
// No-op.
|
||||
(None, None) => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg) {
|
||||
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
||||
let linkage = match read_with_linkage::<_, V>(final_key.as_ref()) {
|
||||
// overwrite but reuse existing linkage
|
||||
Some((_data, linkage)) => linkage,
|
||||
// create new linkage
|
||||
None => new_head_linkage::<_, _, V, G::KeyFormat>(key),
|
||||
};
|
||||
unhashed::put(final_key.as_ref(), &(val, linkage))
|
||||
}
|
||||
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg) {
|
||||
G::take(key);
|
||||
}
|
||||
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R {
|
||||
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
||||
|
||||
let (mut val, _linkage) = read_with_linkage::<K, V>(final_key.as_ref())
|
||||
.map(|(data, linkage)| (G::from_optional_value_to_query(Some(data)), Some(linkage)))
|
||||
.unwrap_or_else(|| (G::from_optional_value_to_query(None), None));
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => G::insert(key, val),
|
||||
None => G::remove(key),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let final_key = Self::storage_linked_map_final_key(key);
|
||||
|
||||
let full_value: Option<(V, Linkage<K>)> = unhashed::take(final_key.as_ref());
|
||||
|
||||
let value = full_value.map(|(data, linkage)| {
|
||||
remove_linkage::<K, V, G::KeyFormat>(linkage);
|
||||
data
|
||||
});
|
||||
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn enumerate() -> Self::Enumerator {
|
||||
Enumerator::<_, _, G::KeyFormat> {
|
||||
next: read_head::<_, G::KeyFormat>(),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn head() -> Option<K> {
|
||||
read_head::<_, G::KeyFormat>()
|
||||
}
|
||||
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len
|
||||
{
|
||||
let key = Self::storage_linked_map_final_key(key);
|
||||
if let Some(v) = unhashed::get_raw(key.as_ref()) {
|
||||
<V as codec::DecodeLength>::len(&v).map_err(|e| e.what())
|
||||
} else {
|
||||
let len = G::from_query_to_optional_value(G::from_optional_value_to_query(None))
|
||||
.map(|v| v.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
/// The translation happens in-place, new keys are inserted at the same time as old keys are
|
||||
/// removed, thus new keys must not collide with still remaining old keys.
|
||||
fn translate<K2, V2, TK, TV>(translate_key: TK, translate_val: TV) -> Result<(), Option<K2>>
|
||||
where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V
|
||||
{
|
||||
let head_key = read_head::<K2, G::KeyFormat>().ok_or(None)?;
|
||||
|
||||
let mut last_key = None;
|
||||
let mut current_key = head_key.clone();
|
||||
|
||||
write_head::<&K, K, G::KeyFormat>(Some(&translate_key(head_key)));
|
||||
|
||||
let translate_linkage = |old: Linkage<K2>| -> Linkage<K> {
|
||||
Linkage {
|
||||
previous: old.previous.map(&translate_key),
|
||||
next: old.next.map(&translate_key),
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let old_raw_key = G::KeyFormat::storage_linked_map_final_key(¤t_key);
|
||||
let x = unhashed::take(old_raw_key.as_ref());
|
||||
let (val, linkage): (V2, Linkage<K2>) = match x {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
// we failed to read value and linkage. Update the last key's linkage
|
||||
// to end the map early, since it's impossible to iterate further.
|
||||
if let Some(last_key) = last_key {
|
||||
let last_raw_key = G::storage_linked_map_final_key(&last_key);
|
||||
if let Some((val, mut linkage))
|
||||
= read_with_linkage::<K, V>(last_raw_key.as_ref())
|
||||
{
|
||||
// defensive: should always happen, since it was just written
|
||||
// in the last iteration of the loop.
|
||||
linkage.next = None;
|
||||
unhashed::put(last_raw_key.as_ref(), &(&val, &linkage));
|
||||
}
|
||||
}
|
||||
|
||||
return Err(Some(current_key));
|
||||
}
|
||||
};
|
||||
let next = linkage.next.clone();
|
||||
|
||||
let val = translate_val(val);
|
||||
let linkage = translate_linkage(linkage);
|
||||
|
||||
// and write in the value and linkage under the new key.
|
||||
let new_key = translate_key(current_key.clone());
|
||||
let new_raw_key = G::storage_linked_map_final_key(&new_key);
|
||||
unhashed::put(new_raw_key.as_ref(), &(&val, &linkage));
|
||||
|
||||
match next {
|
||||
None => break,
|
||||
Some(next) => {
|
||||
last_key = Some(new_key);
|
||||
current_key = next
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,9 @@
|
||||
#[cfg(not(feature = "std"))]
|
||||
use sp_std::prelude::*;
|
||||
use sp_std::borrow::Borrow;
|
||||
use codec::{FullCodec, FullEncode, Encode, EncodeLike, Ref, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, hash::{StorageHasher, Twox128}, traits::Len};
|
||||
use codec::{FullCodec, FullEncode, Decode, Encode, EncodeLike, Ref, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, traits::Len};
|
||||
use crate::hash::{StorageHasher, Twox128, ReversibleStorageHasher};
|
||||
|
||||
/// Generator for `StorageMap` used by `decl_storage`.
|
||||
///
|
||||
@@ -44,6 +45,22 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
/// Storage prefix. Used for generating final key.
|
||||
fn storage_prefix() -> &'static [u8];
|
||||
|
||||
/// The full prefix; just the hash of `module_prefix` concatenated to the hash of
|
||||
/// `storage_prefix`.
|
||||
fn prefix_hash() -> Vec<u8> {
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
|
||||
let mut result = Vec::with_capacity(
|
||||
module_prefix_hashed.len() + storage_prefix_hashed.len()
|
||||
);
|
||||
|
||||
result.extend_from_slice(&module_prefix_hashed[..]);
|
||||
result.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
@@ -51,8 +68,7 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_map_final_key<KeyArg>(key: KeyArg) -> Vec<u8>
|
||||
where
|
||||
fn storage_map_final_key<KeyArg>(key: KeyArg) -> Vec<u8> where
|
||||
KeyArg: EncodeLike<K>,
|
||||
{
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
@@ -71,6 +87,107 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility to iterate through items in a storage map.
|
||||
pub struct StorageMapIterator<K, V, Hasher> {
|
||||
prefix: Vec<u8>,
|
||||
previous_key: Vec<u8>,
|
||||
drain: bool,
|
||||
_phantom: ::sp_std::marker::PhantomData<(K, V, Hasher)>,
|
||||
}
|
||||
|
||||
impl<
|
||||
K: Decode + Sized,
|
||||
V: Decode + Sized,
|
||||
Hasher: ReversibleStorageHasher
|
||||
> Iterator for StorageMapIterator<K, V, Hasher> {
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<(K, V)> {
|
||||
loop {
|
||||
let maybe_next = sp_io::storage::next_key(&self.previous_key)
|
||||
.filter(|n| n.starts_with(&self.prefix));
|
||||
break match maybe_next {
|
||||
Some(next) => {
|
||||
self.previous_key = next;
|
||||
match unhashed::get::<V>(&self.previous_key) {
|
||||
Some(value) => {
|
||||
if self.drain {
|
||||
unhashed::kill(&self.previous_key)
|
||||
}
|
||||
let mut key_material = Hasher::reverse(&self.previous_key[self.prefix.len()..]);
|
||||
match K::decode(&mut key_material) {
|
||||
Ok(key) => Some((key, value)),
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
G: StorageMap<K, V>,
|
||||
> storage::IterableStorageMap<K, V> for G where
|
||||
G::Hasher: ReversibleStorageHasher
|
||||
{
|
||||
type Iterator = StorageMapIterator<K, V, G::Hasher>;
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn iter() -> Self::Iterator {
|
||||
let prefix = G::prefix_hash();
|
||||
Self::Iterator {
|
||||
prefix: prefix.clone(),
|
||||
previous_key: prefix,
|
||||
drain: false,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn drain() -> Self::Iterator {
|
||||
let prefix = G::prefix_hash();
|
||||
Self::Iterator {
|
||||
prefix: prefix.clone(),
|
||||
previous_key: prefix,
|
||||
drain: true,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn translate<O: Decode, F: Fn(K, O) -> Option<V>>(f: F) {
|
||||
let prefix = G::prefix_hash();
|
||||
let mut previous_key = prefix.clone();
|
||||
loop {
|
||||
match sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix)) {
|
||||
Some(next) => {
|
||||
previous_key = next;
|
||||
let maybe_value = unhashed::get::<O>(&previous_key);
|
||||
match maybe_value {
|
||||
Some(value) => {
|
||||
let mut key_material = G::Hasher::reverse(&previous_key[prefix.len()..]);
|
||||
match K::decode(&mut key_material) {
|
||||
Ok(key) => match f(key, value) {
|
||||
Some(new) => unhashed::put::<V>(&previous_key, &new),
|
||||
None => unhashed::kill(&previous_key),
|
||||
},
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
None => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: FullEncode, V: FullCodec, G: StorageMap<K, V>> storage::StorageMap<K, V> for G {
|
||||
type Query = G::Query;
|
||||
|
||||
@@ -228,4 +345,26 @@ impl<K: FullEncode, V: FullCodec, G: StorageMap<K, V>> storage::StorageMap<K, V>
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
fn migrate_key<OldHasher: StorageHasher, KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<V> {
|
||||
let old_key = {
|
||||
let module_prefix_hashed = Twox128::hash(Self::module_prefix());
|
||||
let storage_prefix_hashed = Twox128::hash(Self::storage_prefix());
|
||||
let key_hashed = key.borrow().using_encoded(OldHasher::hash);
|
||||
|
||||
let mut final_key = Vec::with_capacity(
|
||||
module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len()
|
||||
);
|
||||
|
||||
final_key.extend_from_slice(&module_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(&storage_prefix_hashed[..]);
|
||||
final_key.extend_from_slice(key_hashed.as_ref());
|
||||
|
||||
final_key
|
||||
};
|
||||
unhashed::take(old_key.as_ref()).map(|value| {
|
||||
unhashed::put(Self::storage_map_final_key(key).as_ref(), &value);
|
||||
value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,23 +23,20 @@
|
||||
//!
|
||||
//! This is internal api and is subject to change.
|
||||
|
||||
mod linked_map;
|
||||
mod map;
|
||||
mod double_map;
|
||||
mod value;
|
||||
|
||||
pub use linked_map::{StorageLinkedMap, Enumerator, Linkage, KeyFormat as LinkedMapKeyFormat};
|
||||
pub use map::StorageMap;
|
||||
pub use double_map::StorageDoubleMap;
|
||||
pub use value::StorageValue;
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use sp_io::TestExternalities;
|
||||
use codec::{Encode, Decode};
|
||||
use crate::storage::{unhashed, generator::{StorageValue, StorageLinkedMap}};
|
||||
use codec::Encode;
|
||||
use crate::storage::{unhashed, generator::StorageValue, IterableStorageMap};
|
||||
|
||||
struct Runtime {}
|
||||
pub trait Trait {
|
||||
@@ -56,16 +53,10 @@ mod tests {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Clone, Debug, Eq, PartialEq)]
|
||||
struct NumberNumber {
|
||||
a: u32,
|
||||
b: u32,
|
||||
}
|
||||
|
||||
crate::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Runtime {
|
||||
Value get(fn value) config(): (u64, u64);
|
||||
NumberMap: linked_map hasher(blake2_256) NumberNumber => u64;
|
||||
NumberMap: map hasher(identity) u32 => u64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,41 +80,25 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_translate_works() {
|
||||
use super::linked_map::{self, Enumerator, KeyFormat};
|
||||
|
||||
type Format = <NumberMap as StorageLinkedMap<NumberNumber, u64>>::KeyFormat;
|
||||
|
||||
fn map_translate_works() {
|
||||
let t = GenesisConfig::default().build_storage().unwrap();
|
||||
TestExternalities::new(t).execute_with(|| {
|
||||
// start with a map of u32 -> u32.
|
||||
for i in 0u32..100u32 {
|
||||
let final_key = <Format as KeyFormat>::storage_linked_map_final_key(&i);
|
||||
|
||||
let linkage = linked_map::new_head_linkage::<_, u32, u32, Format>(&i);
|
||||
unhashed::put(final_key.as_ref(), &(&i, linkage));
|
||||
unhashed::put(&NumberMap::hashed_key_for(&i), &(i as u64));
|
||||
}
|
||||
|
||||
let head = linked_map::read_head::<u32, Format>().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Enumerator::<u32, u32, Format>::from_head(head).collect::<Vec<_>>(),
|
||||
(0..100).rev().map(|x| (x, x)).collect::<Vec<_>>(),
|
||||
NumberMap::iter().collect::<Vec<_>>(),
|
||||
(0..100).map(|x| (x as u32, x as u64)).collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
// do translation.
|
||||
NumberMap::translate(
|
||||
|k: u32| NumberNumber { a: k, b: k },
|
||||
|v: u32| (v as u64) << 32 | v as u64,
|
||||
).unwrap();
|
||||
NumberMap::translate(|k: u32, v: u64| if k % 2 == 0 { Some((k as u64) << 32 | v) } else { None });
|
||||
|
||||
assert!(linked_map::read_head::<NumberNumber, Format>().is_some());
|
||||
assert_eq!(
|
||||
NumberMap::enumerate().collect::<Vec<_>>(),
|
||||
(0..100u32).rev().map(|x| (
|
||||
NumberNumber { a: x, b: x },
|
||||
(x as u64) << 32 | x as u64,
|
||||
)).collect::<Vec<_>>(),
|
||||
NumberMap::iter().collect::<Vec<_>>(),
|
||||
(0..50u32).map(|x| x * 2).map(|x| (x, (x as u64) << 32 | x as u64)).collect::<Vec<_>>(),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -202,78 +202,60 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
/// function for this purpose.
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len;
|
||||
|
||||
/// Migrate an item with the given `key` from a defunct `OldHasher` to the current hasher.
|
||||
///
|
||||
/// If the key doesn't exist, then it's a no-op. If it does, then it returns its value.
|
||||
fn migrate_key<OldHasher: StorageHasher, KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<V>;
|
||||
|
||||
/// Migrate an item with the given `key` from a `blake2_256` hasher to the current hasher.
|
||||
///
|
||||
/// If the key doesn't exist, then it's a no-op. If it does, then it returns its value.
|
||||
fn migrate_key_from_blake<KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<V> {
|
||||
Self::migrate_key::<crate::hash::Blake2_256, KeyArg>(key)
|
||||
}
|
||||
}
|
||||
|
||||
/// A strongly-typed linked map in storage.
|
||||
///
|
||||
/// Similar to `StorageMap` but allows to enumerate other elements and doesn't implement append.
|
||||
///
|
||||
/// Details on implementation can be found at
|
||||
/// [`generator::StorageLinkedMap`]
|
||||
pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// A strongly-typed map in storage whose keys and values can be iterated over.
|
||||
pub trait IterableStorageMap<K: FullEncode, V: FullCodec>: StorageMap<K, V> {
|
||||
/// The type that iterates over all `(key, value)`.
|
||||
type Enumerator: Iterator<Item = (K, V)>;
|
||||
type Iterator: Iterator<Item = (K, V)>;
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn contains_key<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool;
|
||||
/// Enumerate all elements in the map in no particular order. If you alter the map while doing
|
||||
/// this, you'll get undefined results.
|
||||
fn iter() -> Self::Iterator;
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
/// Remove all elements from the map and iterate through them in no particular order. If you
|
||||
/// add elements to the map while doing this, you'll get undefined results.
|
||||
fn drain() -> Self::Iterator;
|
||||
|
||||
/// Swap the values of two keys.
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2);
|
||||
/// Translate the values of all elements by a function `f`, in the map in no particular order.
|
||||
/// By returning `None` from `f` for an element, you'll remove it from the map.
|
||||
fn translate<O: Decode, F: Fn(K, O) -> Option<V>>(f: F);
|
||||
}
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg);
|
||||
/// A strongly-typed double map in storage whose secondary keys and values can be iterated over.
|
||||
pub trait IterableStorageDoubleMap<
|
||||
K1: FullCodec,
|
||||
K2: FullCodec,
|
||||
V: FullCodec
|
||||
>: StorageDoubleMap<K1, K2, V> {
|
||||
/// The type that iterates over all `(key, value)`.
|
||||
type Iterator: Iterator<Item = (K2, V)>;
|
||||
|
||||
/// Remove the value under a key.
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg);
|
||||
/// Enumerate all elements in the map with first key `k1` in no particular order. If you add or
|
||||
/// remove values whose first key is `k1` to the map while doing this, you'll get undefined
|
||||
/// results.
|
||||
fn iter(k1: impl EncodeLike<K1>) -> Self::Iterator;
|
||||
|
||||
/// Mutate the value under a key.
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R;
|
||||
/// Remove all elements from the map with first key `k1` and iterate through them in no
|
||||
/// particular order. If you add elements with first key `k1` to the map while doing this,
|
||||
/// you'll get undefined results.
|
||||
fn drain(k1: impl EncodeLike<K1>) -> Self::Iterator;
|
||||
|
||||
/// Take the value under a key.
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Return current head element.
|
||||
fn head() -> Option<K>;
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn enumerate() -> Self::Enumerator;
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
|
||||
/// Therefore, this function cannot be used as a sign of _existence_. use the `::contains_key()`
|
||||
/// function for this purpose.
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len;
|
||||
|
||||
/// Translate the keys and values from some previous `(K2, V2)` to the current type.
|
||||
///
|
||||
/// `TK` translates keys from the old type, and `TV` translates values.
|
||||
///
|
||||
/// Returns `Err` if the map could not be interpreted as the old type, and Ok if it could.
|
||||
/// The `Err` contains the first key which could not be migrated, or `None` if the
|
||||
/// head of the list could not be read.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This function must be used with care, before being updated the storage still contains the
|
||||
/// old type, thus other calls (such as `get`) will fail at decoding it.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// This would typically be called inside the module implementation of on_runtime_upgrade, while
|
||||
/// ensuring **no usage of this storage are made before the call to `on_runtime_upgrade`**. (More
|
||||
/// precisely prior initialized modules doesn't make use of this storage).
|
||||
fn translate<K2, V2, TK, TV>(translate_key: TK, translate_val: TV) -> Result<(), Option<K2>>
|
||||
where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V;
|
||||
/// Translate the values of all elements by a function `f`, in the map in no particular order.
|
||||
/// By returning `None` from `f` for an element, you'll remove it from the map.
|
||||
fn translate<O: Decode, F: Fn(O) -> Option<V>>(f: F);
|
||||
}
|
||||
|
||||
/// An implementation of a map with a two keys.
|
||||
@@ -377,6 +359,17 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
V: codec::DecodeLength + Len;
|
||||
|
||||
/// Migrate an item with the given `key1` and `key2` from defunct `OldHasher1` and
|
||||
/// `OldHasher2` to the current hashers.
|
||||
///
|
||||
/// If the key doesn't exist, then it's a no-op. If it does, then it returns its value.
|
||||
fn migrate_keys<
|
||||
OldHasher1: StorageHasher,
|
||||
OldHasher2: StorageHasher,
|
||||
KeyArg1: EncodeLike<K1>,
|
||||
KeyArg2: EncodeLike<K2>,
|
||||
>(key1: KeyArg1, key2: KeyArg2) -> Option<V>;
|
||||
}
|
||||
|
||||
/// Iterator for prefixed map.
|
||||
@@ -440,7 +433,7 @@ pub trait StoragePrefixedMap<Value: FullCodec> {
|
||||
}
|
||||
|
||||
/// Iter over all value of the storage.
|
||||
fn iter() -> PrefixIterator<Value> {
|
||||
fn iter_values() -> PrefixIterator<Value> {
|
||||
let prefix = Self::final_prefix();
|
||||
PrefixIterator {
|
||||
prefix: prefix.to_vec(),
|
||||
@@ -535,26 +528,26 @@ mod test {
|
||||
assert_eq!(MyStorage::final_prefix().to_vec(), k);
|
||||
|
||||
// test iteration
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![]);
|
||||
|
||||
unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u64);
|
||||
unhashed::put(&[&k[..], &vec![1, 1][..]].concat(), &2u64);
|
||||
unhashed::put(&[&k[..], &vec![8][..]].concat(), &3u64);
|
||||
unhashed::put(&[&k[..], &vec![10][..]].concat(), &4u64);
|
||||
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 2, 3, 4]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 2, 3, 4]);
|
||||
|
||||
// test removal
|
||||
MyStorage::remove_all();
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![]);
|
||||
|
||||
// test migration
|
||||
unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u32);
|
||||
unhashed::put(&[&k[..], &vec![8][..]].concat(), &2u32);
|
||||
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![]);
|
||||
MyStorage::translate_values(|v: u32| v as u64).unwrap();
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 2]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 2]);
|
||||
MyStorage::remove_all();
|
||||
|
||||
// test migration 2
|
||||
@@ -564,9 +557,9 @@ mod test {
|
||||
unhashed::put(&[&k[..], &vec![10][..]].concat(), &4u32);
|
||||
|
||||
// (contains some value that successfully decoded to u64)
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 2, 3]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 2, 3]);
|
||||
assert_eq!(MyStorage::translate_values(|v: u128| v as u64), Err(2));
|
||||
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 3]);
|
||||
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 3]);
|
||||
MyStorage::remove_all();
|
||||
|
||||
// test that other values are not modified.
|
||||
|
||||
@@ -26,10 +26,16 @@ use sp_runtime::{
|
||||
ConsensusEngineId, DispatchResult, DispatchError,
|
||||
traits::{MaybeSerializeDeserialize, AtLeast32Bit, Saturating, TrailingZeroInput},
|
||||
};
|
||||
|
||||
use crate::dispatch::Parameter;
|
||||
use crate::storage::StorageMap;
|
||||
|
||||
/// Migrate a given account.
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
pub trait MigrateAccount<A> {
|
||||
/// Migrate the `account`.
|
||||
fn migrate_account(account: &A);
|
||||
}
|
||||
|
||||
/// An abstraction of a value stored within storage, but possibly as part of a larger composite
|
||||
/// item.
|
||||
pub trait StoredMap<K, T> {
|
||||
|
||||
@@ -37,10 +37,10 @@ mod tests {
|
||||
// non-getters: pub / $default
|
||||
|
||||
/// Hello, this is doc!
|
||||
U32 : Option<u32>;
|
||||
pub PUBU32 : Option<u32>;
|
||||
U32MYDEF : Option<u32>;
|
||||
pub PUBU32MYDEF : Option<u32>;
|
||||
U32: Option<u32>;
|
||||
pub PUBU32: Option<u32>;
|
||||
U32MYDEF: Option<u32>;
|
||||
pub PUBU32MYDEF: Option<u32>;
|
||||
|
||||
// getters: pub / $default
|
||||
// we need at least one type which uses T, otherwise GenesisConfig will complain.
|
||||
@@ -59,31 +59,23 @@ mod tests {
|
||||
GetOptU32WithBuilderNone get(fn opt_u32_with_builder_none) build(|_| None): Option<u32>;
|
||||
|
||||
// map non-getters: pub / $default
|
||||
MAPU32 : map hasher(blake2_256) u32 => Option<String>;
|
||||
pub PUBMAPU32 : map hasher(blake2_256) u32 => Option<String>;
|
||||
MAPU32MYDEF : map hasher(blake2_256) u32 => Option<String>;
|
||||
pub PUBMAPU32MYDEF : map hasher(blake2_256) u32 => Option<String>;
|
||||
MAPU32: map hasher(blake2_128_concat) u32 => Option<String>;
|
||||
pub PUBMAPU32: map hasher(blake2_128_concat) u32 => Option<String>;
|
||||
MAPU32MYDEF: map hasher(blake2_128_concat) u32 => Option<String>;
|
||||
pub PUBMAPU32MYDEF: map hasher(blake2_128_concat) u32 => Option<String>;
|
||||
|
||||
// map getters: pub / $default
|
||||
GETMAPU32 get(fn map_u32_getter): map hasher(blake2_256) u32 => String;
|
||||
pub PUBGETMAPU32 get(fn pub_map_u32_getter): map hasher(blake2_256) u32 => String;
|
||||
GETMAPU32 get(fn map_u32_getter): map hasher(blake2_128_concat) u32 => String;
|
||||
pub PUBGETMAPU32 get(fn pub_map_u32_getter): map hasher(blake2_128_concat) u32 => String;
|
||||
|
||||
GETMAPU32MYDEF get(fn map_u32_getter_mydef):
|
||||
map hasher(blake2_256) u32 => String = "map".into();
|
||||
map hasher(blake2_128_concat) u32 => String = "map".into();
|
||||
pub PUBGETMAPU32MYDEF get(fn pub_map_u32_getter_mydef):
|
||||
map hasher(blake2_256) u32 => String = "pubmap".into();
|
||||
|
||||
// linked map
|
||||
LINKEDMAPU32 : linked_map hasher(blake2_256) u32 => Option<String>;
|
||||
pub PUBLINKEDMAPU32MYDEF : linked_map hasher(blake2_256) u32 => Option<String>;
|
||||
GETLINKEDMAPU32 get(fn linked_map_u32_getter):
|
||||
linked_map hasher(blake2_256) u32 => String;
|
||||
pub PUBGETLINKEDMAPU32MYDEF get(fn pub_linked_map_u32_getter_mydef):
|
||||
linked_map hasher(blake2_256) u32 => String = "pubmap".into();
|
||||
map hasher(blake2_128_concat) u32 => String = "pubmap".into();
|
||||
|
||||
COMPLEXTYPE1: ::std::vec::Vec<<T as Trait>::Origin>;
|
||||
COMPLEXTYPE2: (Vec<Vec<(u16,Box<( )>)>>, u32);
|
||||
COMPLEXTYPE3: [u32;25];
|
||||
COMPLEXTYPE2: (Vec<Vec<(u16, Box<()>)>>, u32);
|
||||
COMPLEXTYPE3: [u32; 25];
|
||||
}
|
||||
add_extra_genesis {
|
||||
build(|_| {});
|
||||
@@ -249,10 +241,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("MAPU32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructMAPU32(PhantomData::<TraitImpl>))
|
||||
@@ -263,10 +255,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("PUBMAPU32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBMAPU32(PhantomData::<TraitImpl>))
|
||||
@@ -277,10 +269,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("MAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
@@ -291,10 +283,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("PUBMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
@@ -305,10 +297,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GETMAPU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETMAPU32(PhantomData::<TraitImpl>))
|
||||
@@ -319,10 +311,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("PUBGETMAPU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETMAPU32(PhantomData::<TraitImpl>))
|
||||
@@ -333,10 +325,10 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("GETMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
@@ -347,72 +339,16 @@ mod tests {
|
||||
name: DecodeDifferent::Encode("PUBGETMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Blake2_128Concat,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("LINKEDMAPU32"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructLINKEDMAPU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBLINKEDMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBLINKEDMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("GETLINKEDMAPU32"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGETLINKEDMAPU32(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("PUBGETLINKEDMAPU32MYDEF"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u32"),
|
||||
value: DecodeDifferent::Encode("String"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructPUBGETLINKEDMAPU32MYDEF(PhantomData::<TraitImpl>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("COMPLEXTYPE1"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
@@ -562,17 +498,13 @@ mod test_append_and_len {
|
||||
JustVecWithDefault: Vec<u32> = vec![6, 9];
|
||||
OptionVec: Option<Vec<u32>>;
|
||||
|
||||
MapVec: map hasher(blake2_256) u32 => Vec<u32>;
|
||||
MapVecWithDefault: map hasher(blake2_256) u32 => Vec<u32> = vec![6, 9];
|
||||
OptionMapVec: map hasher(blake2_256) u32 => Option<Vec<u32>>;
|
||||
MapVec: map hasher(blake2_128_concat) u32 => Vec<u32>;
|
||||
MapVecWithDefault: map hasher(blake2_128_concat) u32 => Vec<u32> = vec![6, 9];
|
||||
OptionMapVec: map hasher(blake2_128_concat) u32 => Option<Vec<u32>>;
|
||||
|
||||
DoubleMapVec: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => Vec<u32>;
|
||||
DoubleMapVecWithDefault: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => Vec<u32> = vec![6, 9];
|
||||
OptionDoubleMapVec: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => Option<Vec<u32>>;
|
||||
|
||||
LinkedMapVec: linked_map hasher(blake2_256) u32 => Vec<u32>;
|
||||
LinkedMapVecWithDefault: linked_map hasher(blake2_256) u32 => Vec<u32> = vec![6, 9];
|
||||
OptionLinkedMapVec: linked_map hasher(blake2_256) u32 => Option<Vec<u32>>;
|
||||
DoubleMapVec: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Vec<u32>;
|
||||
DoubleMapVecWithDefault: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Vec<u32> = vec![6, 9];
|
||||
OptionDoubleMapVec: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => Option<Vec<u32>>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,13 +576,11 @@ mod test_append_and_len {
|
||||
JustVec::put(&vec![1, 2, 3, 4]);
|
||||
OptionVec::put(&vec![1, 2, 3, 4, 5]);
|
||||
MapVec::insert(1, &vec![1, 2, 3, 4, 5, 6]);
|
||||
LinkedMapVec::insert(2, &vec![1, 2, 3]);
|
||||
DoubleMapVec::insert(0, 1, &vec![1, 2]);
|
||||
|
||||
assert_eq!(JustVec::decode_len().unwrap(), 4);
|
||||
assert_eq!(OptionVec::decode_len().unwrap(), 5);
|
||||
assert_eq!(MapVec::decode_len(1).unwrap(), 6);
|
||||
assert_eq!(LinkedMapVec::decode_len(2).unwrap(), 3);
|
||||
assert_eq!(DoubleMapVec::decode_len(0, 1).unwrap(), 2);
|
||||
});
|
||||
}
|
||||
@@ -678,16 +608,6 @@ mod test_append_and_len {
|
||||
assert_eq!(OptionMapVec::get(0), None);
|
||||
assert_eq!(OptionMapVec::decode_len(0), Ok(0));
|
||||
|
||||
// linked map
|
||||
assert_eq!(LinkedMapVec::get(0), vec![]);
|
||||
assert_eq!(LinkedMapVec::decode_len(0), Ok(0));
|
||||
|
||||
assert_eq!(LinkedMapVecWithDefault::get(0), vec![6, 9]);
|
||||
assert_eq!(LinkedMapVecWithDefault::decode_len(0), Ok(2));
|
||||
|
||||
assert_eq!(OptionLinkedMapVec::get(0), None);
|
||||
assert_eq!(OptionLinkedMapVec::decode_len(0), Ok(0));
|
||||
|
||||
// Double map
|
||||
assert_eq!(DoubleMapVec::get(0, 0), vec![]);
|
||||
assert_eq!(DoubleMapVec::decode_len(0, 1), Ok(0));
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
use frame_support::storage::unhashed;
|
||||
use codec::Encode;
|
||||
use frame_support::{StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue, StoragePrefixedMap};
|
||||
use sp_io::{TestExternalities, hashing::{twox_128, blake2_128, blake2_256}};
|
||||
use frame_support::{StorageDoubleMap, StorageMap, StorageValue, StoragePrefixedMap};
|
||||
use sp_io::{TestExternalities, hashing::{twox_64, twox_128, blake2_128}};
|
||||
|
||||
mod no_instance {
|
||||
use codec::{Encode, Decode, EncodeLike};
|
||||
@@ -35,18 +35,15 @@ mod no_instance {
|
||||
trait Store for Module<T: Trait> as FinalKeysNone {
|
||||
pub Value config(value): u32;
|
||||
|
||||
pub Map: map hasher(blake2_256) u32 => u32;
|
||||
pub Map2: map hasher(twox_128) u32 => u32;
|
||||
pub Map: map hasher(blake2_128_concat) u32 => u32;
|
||||
pub Map2: map hasher(twox_64_concat) u32 => u32;
|
||||
|
||||
pub LinkedMap: linked_map hasher(blake2_256) u32 => u32;
|
||||
pub LinkedMap2: linked_map hasher(twox_128) u32 => u32;
|
||||
|
||||
pub DoubleMap: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => u32;
|
||||
pub DoubleMap2: double_map hasher(twox_128) u32, hasher(blake2_128) u32 => u32;
|
||||
pub DoubleMap: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => u32;
|
||||
pub DoubleMap2: double_map hasher(twox_64_concat) u32, hasher(twox_64_concat) u32 => u32;
|
||||
|
||||
pub TestGenericValue get(fn test_generic_value) config(): Option<T::BlockNumber>;
|
||||
pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map):
|
||||
double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Option<u32>;
|
||||
double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) T::BlockNumber => Option<u32>;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,18 +62,15 @@ mod instance {
|
||||
{
|
||||
pub Value config(value): u32;
|
||||
|
||||
pub Map: map hasher(blake2_256) u32 => u32;
|
||||
pub Map2: map hasher(twox_128) u32 => u32;
|
||||
pub Map: map hasher(blake2_128_concat) u32 => u32;
|
||||
pub Map2: map hasher(twox_64_concat) u32 => u32;
|
||||
|
||||
pub LinkedMap: linked_map hasher(blake2_256) u32 => u32;
|
||||
pub LinkedMap2: linked_map hasher(twox_128) u32 => u32;
|
||||
|
||||
pub DoubleMap: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => u32;
|
||||
pub DoubleMap2: double_map hasher(twox_128) u32, hasher(blake2_128) u32 => u32;
|
||||
pub DoubleMap: double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) u32 => u32;
|
||||
pub DoubleMap2: double_map hasher(twox_64_concat) u32, hasher(twox_64_concat) u32 => u32;
|
||||
|
||||
pub TestGenericValue get(fn test_generic_value) config(): Option<T::BlockNumber>;
|
||||
pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map):
|
||||
double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Option<u32>;
|
||||
double_map hasher(blake2_128_concat) u32, hasher(blake2_128_concat) T::BlockNumber => Option<u32>;
|
||||
}
|
||||
add_extra_genesis {
|
||||
// See `decl_storage` limitation.
|
||||
@@ -85,6 +79,18 @@ mod instance {
|
||||
}
|
||||
}
|
||||
|
||||
fn twox_64_concat(d: &[u8]) -> Vec<u8> {
|
||||
let mut v = twox_64(d).to_vec();
|
||||
v.extend_from_slice(d);
|
||||
v
|
||||
}
|
||||
|
||||
fn blake2_128_concat(d: &[u8]) -> Vec<u8> {
|
||||
let mut v = blake2_128(d).to_vec();
|
||||
v.extend_from_slice(d);
|
||||
v
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn final_keys_no_instance() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
@@ -94,41 +100,27 @@ fn final_keys_no_instance() {
|
||||
|
||||
no_instance::Map::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"Map")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<no_instance::Map>::final_prefix());
|
||||
|
||||
no_instance::Map2::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"Map2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<no_instance::Map2>::final_prefix());
|
||||
|
||||
let head = [twox_128(b"FinalKeysNone"), twox_128(b"HeadOfLinkedMap")].concat();
|
||||
assert_eq!(unhashed::get::<u32>(&head), None);
|
||||
|
||||
no_instance::LinkedMap::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"LinkedMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(unhashed::get::<u32>(&head), Some(1u32));
|
||||
|
||||
no_instance::LinkedMap2::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"LinkedMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
|
||||
no_instance::DoubleMap::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"DoubleMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
k.extend(2u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<no_instance::DoubleMap>::final_prefix());
|
||||
|
||||
no_instance::DoubleMap2::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"DoubleMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
k.extend(2u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<no_instance::DoubleMap2>::final_prefix());
|
||||
});
|
||||
@@ -143,41 +135,27 @@ fn final_keys_default_instance() {
|
||||
|
||||
<instance::Map<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"Map")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<instance::Map<instance::DefaultInstance>>::final_prefix());
|
||||
|
||||
<instance::Map2<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"Map2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<instance::Map2<instance::DefaultInstance>>::final_prefix());
|
||||
|
||||
let head = [twox_128(b"FinalKeysSome"), twox_128(b"HeadOfLinkedMap")].concat();
|
||||
assert_eq!(unhashed::get::<u32>(&head), None);
|
||||
|
||||
<instance::LinkedMap<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"LinkedMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(unhashed::get::<u32>(&head), Some(1u32));
|
||||
|
||||
<instance::LinkedMap2<instance::DefaultInstance>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"LinkedMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
|
||||
<instance::DoubleMap<instance::DefaultInstance>>::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"DoubleMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
k.extend(2u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<instance::DoubleMap<instance::DefaultInstance>>::final_prefix());
|
||||
|
||||
<instance::DoubleMap2<instance::DefaultInstance>>::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"DoubleMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
k.extend(2u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<instance::DoubleMap2<instance::DefaultInstance>>::final_prefix());
|
||||
});
|
||||
@@ -192,41 +170,27 @@ fn final_keys_instance_2() {
|
||||
|
||||
<instance::Map<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"Map")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<instance::Map<instance::Instance2>>::final_prefix());
|
||||
|
||||
<instance::Map2<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"Map2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(&k[..32], &<instance::Map2<instance::Instance2>>::final_prefix());
|
||||
|
||||
let head = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"HeadOfLinkedMap")].concat();
|
||||
assert_eq!(unhashed::get::<u32>(&head), None);
|
||||
|
||||
<instance::LinkedMap<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"LinkedMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
assert_eq!(unhashed::get::<u32>(&head), Some(1u32));
|
||||
|
||||
<instance::LinkedMap2<instance::Instance2>>::insert(1, 2);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"LinkedMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(2u32));
|
||||
|
||||
<instance::DoubleMap<instance::Instance2>>::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"DoubleMap")].concat();
|
||||
k.extend(1u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_256).to_vec());
|
||||
k.extend(1u32.using_encoded(blake2_128_concat));
|
||||
k.extend(2u32.using_encoded(blake2_128_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<instance::DoubleMap<instance::Instance2>>::final_prefix());
|
||||
|
||||
<instance::DoubleMap2<instance::Instance2>>::insert(&1, &2, &3);
|
||||
let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"DoubleMap2")].concat();
|
||||
k.extend(1u32.using_encoded(twox_128).to_vec());
|
||||
k.extend(2u32.using_encoded(blake2_128).to_vec());
|
||||
k.extend(1u32.using_encoded(twox_64_concat));
|
||||
k.extend(2u32.using_encoded(twox_64_concat));
|
||||
assert_eq!(unhashed::get::<u32>(&k), Some(3u32));
|
||||
assert_eq!(&k[..32], &<instance::DoubleMap2<instance::Instance2>>::final_prefix());
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ frame_support::decl_module! {
|
||||
|
||||
frame_support::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Test {
|
||||
pub AppendableDM config(t): double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Vec<u32>;
|
||||
pub AppendableDM config(t): double_map hasher(identity) u32, hasher(identity) T::BlockNumber => Vec<u32>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ use frame_support::{
|
||||
DecodeDifferent, StorageMetadata, StorageEntryModifier, StorageEntryType, DefaultByteGetter,
|
||||
StorageEntryMetadata, StorageHasher,
|
||||
},
|
||||
StorageValue, StorageMap, StorageLinkedMap, StorageDoubleMap,
|
||||
StorageValue, StorageMap, StorageDoubleMap,
|
||||
};
|
||||
use sp_inherents::{ProvideInherent, InherentData, InherentIdentifier, MakeFatalError};
|
||||
use sp_core::{H256, sr25519};
|
||||
@@ -67,8 +67,7 @@ mod module1 {
|
||||
T::BlockNumber: From<u32> + std::fmt::Display
|
||||
{
|
||||
pub Value config(value): T::GenericType;
|
||||
pub Map: map hasher(blake2_256) u32 => u64;
|
||||
pub LinkedMap: linked_map hasher(blake2_256) u32 => u64;
|
||||
pub Map: map hasher(identity) u32 => u64;
|
||||
}
|
||||
|
||||
add_extra_genesis {
|
||||
@@ -136,9 +135,8 @@ mod module2 {
|
||||
frame_support::decl_storage! {
|
||||
trait Store for Module<T: Trait<I>, I: Instance=DefaultInstance> as Module2 {
|
||||
pub Value config(value): T::Amount;
|
||||
pub Map config(map): map hasher(blake2_256) u64 => u64;
|
||||
pub LinkedMap config(linked_map): linked_map hasher(blake2_256) u64 => Vec<u8>;
|
||||
pub DoubleMap config(double_map): double_map hasher(blake2_256) u64, hasher(blake2_256) u64 => u64;
|
||||
pub Map config(map): map hasher(identity) u64 => u64;
|
||||
pub DoubleMap config(double_map): double_map hasher(identity) u64, hasher(identity) u64 => u64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,13 +283,11 @@ fn new_test_ext() -> sp_io::TestExternalities {
|
||||
module2: Some(module2::GenesisConfig {
|
||||
value: 4,
|
||||
map: vec![(0, 0)],
|
||||
linked_map: vec![(0, vec![0])],
|
||||
double_map: vec![(0, 0, 0)],
|
||||
}),
|
||||
module2_Instance1: Some(module2::GenesisConfig {
|
||||
value: 4,
|
||||
map: vec![(0, 0)],
|
||||
linked_map: vec![(0, vec![0])],
|
||||
double_map: vec![(0, 0, 0)],
|
||||
}),
|
||||
module2_Instance2: None,
|
||||
@@ -314,17 +310,13 @@ fn storage_instance_independence() {
|
||||
module2::Map::<module2::Instance1>::insert(0, 0);
|
||||
module2::Map::<module2::Instance2>::insert(0, 0);
|
||||
module2::Map::<module2::Instance3>::insert(0, 0);
|
||||
module2::LinkedMap::<module2::DefaultInstance>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::LinkedMap::<module2::Instance1>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::LinkedMap::<module2::Instance2>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::LinkedMap::<module2::Instance3>::insert::<_, Vec<u8>>(0, vec![]);
|
||||
module2::DoubleMap::<module2::DefaultInstance>::insert(&0, &0, &0);
|
||||
module2::DoubleMap::<module2::Instance1>::insert(&0, &0, &0);
|
||||
module2::DoubleMap::<module2::Instance2>::insert(&0, &0, &0);
|
||||
module2::DoubleMap::<module2::Instance3>::insert(&0, &0, &0);
|
||||
});
|
||||
// 16 storage values + 4 linked_map head.
|
||||
assert_eq!(storage.top.len(), 16 + 4);
|
||||
// 12 storage values.
|
||||
assert_eq!(storage.top.len(), 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -332,7 +324,6 @@ fn storage_with_instance_basic_operation() {
|
||||
new_test_ext().execute_with(|| {
|
||||
type Value = module2::Value<Runtime, module2::Instance1>;
|
||||
type Map = module2::Map<module2::Instance1>;
|
||||
type LinkedMap = module2::LinkedMap<module2::Instance1>;
|
||||
type DoubleMap = module2::DoubleMap<module2::Instance1>;
|
||||
|
||||
assert_eq!(Value::exists(), true);
|
||||
@@ -360,26 +351,6 @@ fn storage_with_instance_basic_operation() {
|
||||
assert_eq!(Map::contains_key(key), false);
|
||||
assert_eq!(Map::get(key), 0);
|
||||
|
||||
assert_eq!(LinkedMap::contains_key(0), true);
|
||||
assert_eq!(LinkedMap::contains_key(key), false);
|
||||
LinkedMap::insert(key, vec![1]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 2);
|
||||
assert_eq!(LinkedMap::get(key), vec![1]);
|
||||
assert_eq!(LinkedMap::take(key), vec![1]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 1);
|
||||
assert_eq!(LinkedMap::get(key), vec![]);
|
||||
LinkedMap::mutate(key, |a| *a=vec![2]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 2);
|
||||
assert_eq!(LinkedMap::get(key), vec![2]);
|
||||
LinkedMap::remove(key);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 1);
|
||||
assert_eq!(LinkedMap::contains_key(key), false);
|
||||
assert_eq!(LinkedMap::get(key), vec![]);
|
||||
assert_eq!(LinkedMap::contains_key(key), false);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 1);
|
||||
LinkedMap::insert(key, &vec![1]);
|
||||
assert_eq!(LinkedMap::enumerate().count(), 2);
|
||||
|
||||
let key1 = 1;
|
||||
let key2 = 1;
|
||||
assert_eq!(DoubleMap::contains_key(&0, &0), true);
|
||||
@@ -416,10 +387,10 @@ const EXPECTED_METADATA: StorageMetadata = StorageMetadata {
|
||||
name: DecodeDifferent::Encode("Map"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Identity,
|
||||
key: DecodeDifferent::Encode("u64"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
is_linked: false,
|
||||
unused: false,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(
|
||||
@@ -430,30 +401,12 @@ const EXPECTED_METADATA: StorageMetadata = StorageMetadata {
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("LinkedMap"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Map {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key: DecodeDifferent::Encode("u64"),
|
||||
value: DecodeDifferent::Encode("Vec<u8>"),
|
||||
is_linked: true,
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(
|
||||
&module2::__GetByteStructLinkedMap(
|
||||
std::marker::PhantomData::<(Runtime, module2::Instance2)>
|
||||
)
|
||||
)
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: DecodeDifferent::Encode("DoubleMap"),
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::DoubleMap {
|
||||
hasher: StorageHasher::Blake2_256,
|
||||
key2_hasher: StorageHasher::Blake2_256,
|
||||
hasher: StorageHasher::Identity,
|
||||
key2_hasher: StorageHasher::Identity,
|
||||
key1: DecodeDifferent::Encode("u64"),
|
||||
key2: DecodeDifferent::Encode("u64"),
|
||||
value: DecodeDifferent::Encode("u64"),
|
||||
|
||||
@@ -109,7 +109,7 @@ mod module {
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}): map hasher(blake2_256) Role => Option<RoleParameters<T>>;
|
||||
}): map hasher(blake2_128_concat) Role => Option<RoleParameters<T>>;
|
||||
|
||||
/// the roles members can enter into
|
||||
pub AvailableRoles get(fn available_roles) build(|config: &GenesisConfig| {
|
||||
@@ -125,11 +125,11 @@ mod module {
|
||||
|
||||
/// actor accounts associated with a role
|
||||
pub AccountIdsByRole get(fn account_ids_by_role):
|
||||
map hasher(blake2_256) Role => Vec<T::AccountId>;
|
||||
map hasher(blake2_128_concat) Role => Vec<T::AccountId>;
|
||||
|
||||
/// tokens locked until given block number
|
||||
pub Bondage get(fn bondage):
|
||||
map hasher(blake2_256) T::AccountId => T::BlockNumber;
|
||||
map hasher(blake2_128_concat) T::AccountId => T::BlockNumber;
|
||||
|
||||
/// First step before enter a role is registering intent with a new account/key.
|
||||
/// This is done by sending a role_entry_request() from the new account.
|
||||
|
||||
@@ -77,7 +77,7 @@ impl system::Trait for Runtime {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -116,7 +116,7 @@ use frame_support::{
|
||||
decl_module, decl_event, decl_storage, decl_error, storage, Parameter, ensure, debug,
|
||||
traits::{
|
||||
Contains, Get, ModuleToIndex, OnNewAccount, OnKilledAccount, IsDeadAccount, Happened,
|
||||
StoredMap
|
||||
StoredMap, MigrateAccount,
|
||||
},
|
||||
weights::{Weight, DispatchInfo, DispatchClass, SimpleDispatchInfo, FunctionOf},
|
||||
};
|
||||
@@ -126,6 +126,7 @@ use codec::{Encode, Decode, FullCodec, EncodeLike};
|
||||
use sp_io::TestExternalities;
|
||||
|
||||
pub mod offchain;
|
||||
mod migration;
|
||||
|
||||
/// Compute the trie root of a list of extrinsics.
|
||||
pub fn extrinsics_root<H: Hash, E: codec::Encode>(extrinsics: &[E]) -> H::Output {
|
||||
@@ -221,6 +222,9 @@ pub trait Trait: 'static + Eq + Clone {
|
||||
///
|
||||
/// All resources should be cleaned up associated with the given account.
|
||||
type OnKilledAccount: OnKilledAccount<Self::AccountId>;
|
||||
|
||||
/// Migrate an account.
|
||||
type MigrateAccount: MigrateAccount<Self::AccountId>;
|
||||
}
|
||||
|
||||
pub type DigestOf<T> = generic::Digest<<T as Trait>::Hash>;
|
||||
@@ -337,10 +341,8 @@ impl From<sp_version::RuntimeVersion> for LastRuntimeUpgradeInfo {
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as System {
|
||||
/// The full account information for a particular account ID.
|
||||
// TODO: should be hasher(twox64_concat) - will need staged migration
|
||||
// https://github.com/paritytech/substrate/issues/4917
|
||||
pub Account get(fn account):
|
||||
map hasher(blake2_256) T::AccountId => AccountInfo<T::Index, T::AccountData>;
|
||||
map hasher(blake2_128_concat) T::AccountId => AccountInfo<T::Index, T::AccountData>;
|
||||
|
||||
/// Total extrinsics count for the current block.
|
||||
ExtrinsicCount: Option<u32>;
|
||||
@@ -352,10 +354,8 @@ decl_storage! {
|
||||
AllExtrinsicsLen: Option<u32>;
|
||||
|
||||
/// Map of block numbers to block hashes.
|
||||
// TODO: should be hasher(twox64_concat) - will need one-off migration
|
||||
// https://github.com/paritytech/substrate/issues/4917
|
||||
pub BlockHash get(fn block_hash) build(|_| vec![(T::BlockNumber::zero(), hash69())]):
|
||||
map hasher(blake2_256) T::BlockNumber => T::Hash;
|
||||
map hasher(twox_64_concat) T::BlockNumber => T::Hash;
|
||||
|
||||
/// Extrinsics data for the current block (maps an extrinsic's index to its data).
|
||||
ExtrinsicData get(fn extrinsic_data): map hasher(twox_64_concat) u32 => Vec<u8>;
|
||||
@@ -393,7 +393,7 @@ decl_storage! {
|
||||
/// The value has the type `(T::BlockNumber, EventIndex)` because if we used only just
|
||||
/// the `EventIndex` then in case if the topic has the same contents on the next block
|
||||
/// no notification will be triggered thus the event might be lost.
|
||||
EventTopics get(fn event_topics): map hasher(blake2_256) T::Hash => Vec<(T::BlockNumber, EventIndex)>;
|
||||
EventTopics get(fn event_topics): map hasher(blake2_128_concat) T::Hash => Vec<(T::BlockNumber, EventIndex)>;
|
||||
|
||||
/// Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened.
|
||||
pub LastRuntimeUpgrade build(|_| Some(LastRuntimeUpgradeInfo::from(T::Version::get()))): Option<LastRuntimeUpgradeInfo>;
|
||||
@@ -452,7 +452,7 @@ decl_error! {
|
||||
/// Suicide called when the account has non-default composite data.
|
||||
NonDefaultComposite,
|
||||
/// There is a non-zero reference count preventing the account from being purged.
|
||||
NonZeroRefCount
|
||||
NonZeroRefCount,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,6 +460,15 @@ decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
type Error = Error<T>;
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
migration::migrate::<T>();
|
||||
|
||||
// Remove the old `RuntimeUpgraded` storage entry.
|
||||
let mut runtime_upgraded_key = sp_io::hashing::twox_128(b"System").to_vec();
|
||||
runtime_upgraded_key.extend(&sp_io::hashing::twox_128(b"RuntimeUpgraded"));
|
||||
sp_io::storage::clear(&runtime_upgraded_key);
|
||||
}
|
||||
|
||||
/// A dispatch that will fill the block weight up to the given ratio.
|
||||
// TODO: This should only be available for testing, rather than in general usage, but
|
||||
// that's not possible at present (since it's within the decl_module macro).
|
||||
@@ -569,11 +578,19 @@ decl_module! {
|
||||
Account::<T>::remove(who);
|
||||
}
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
// Remove the old `RuntimeUpgraded` storage entry.
|
||||
let mut runtime_upgraded_key = sp_io::hashing::twox_128(b"System").to_vec();
|
||||
runtime_upgraded_key.extend(&sp_io::hashing::twox_128(b"RuntimeUpgraded"));
|
||||
sp_io::storage::clear(&runtime_upgraded_key);
|
||||
#[weight = FunctionOf(
|
||||
|(accounts,): (&Vec<T::AccountId>,)| accounts.len() as u32 * 10_000,
|
||||
DispatchClass::Normal,
|
||||
true,
|
||||
)]
|
||||
fn migrate_accounts(origin, accounts: Vec<T::AccountId>) {
|
||||
let _ = ensure_signed(origin)?;
|
||||
for a in &accounts {
|
||||
if Account::<T>::migrate_key_from_blake(a).is_some() {
|
||||
// Inform other modules about the account.
|
||||
T::MigrateAccount::migrate_account(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1553,7 +1570,7 @@ mod tests {
|
||||
type Version = Version;
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = u32;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = RecordKilled;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
use super::*;
|
||||
use sp_runtime::traits::SaturatedConversion;
|
||||
|
||||
pub fn migrate<T: Trait>() {
|
||||
// Number is current block - we obviously don't know that hash.
|
||||
// Number - 1 is the parent block, who hash we record in this block, but then that's already
|
||||
// with the new storage so we don't migrate it.
|
||||
// Number - 2 is therefore the most recent block's hash that needs migrating.
|
||||
if Number::<T>::get() > One::one() {
|
||||
sp_runtime::print("Migrating BlockHash...");
|
||||
BlockHash::<T>::migrate_key_from_blake(T::BlockNumber::zero());
|
||||
let mut n = Number::<T>::get() - One::one() - One::one();
|
||||
while !n.is_zero() {
|
||||
sp_runtime::print(n.saturated_into::<u32>());
|
||||
if BlockHash::<T>::migrate_key_from_blake(n).is_none() {
|
||||
break;
|
||||
}
|
||||
n -= One::one();
|
||||
}
|
||||
}
|
||||
|
||||
sp_runtime::print("Migrating Accounts...");
|
||||
let mut count = 0u32;
|
||||
if let Ok(accounts) = Vec::<T::AccountId>::decode(&mut &include_bytes!("accounts.scale")[..]) {
|
||||
for a in &accounts {
|
||||
if Account::<T>::migrate_key_from_blake(a).is_some() {
|
||||
// Inform other modules about the account.
|
||||
T::MigrateAccount::migrate_account(a);
|
||||
count += 1;
|
||||
if count % 1000 == 0 {
|
||||
sp_runtime::print(count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sp_runtime::print(count);
|
||||
}
|
||||
@@ -278,7 +278,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -48,8 +48,6 @@ use sp_runtime::{
|
||||
};
|
||||
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
|
||||
|
||||
mod migration;
|
||||
|
||||
type Multiplier = Fixed64;
|
||||
type BalanceOf<T> =
|
||||
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
|
||||
@@ -97,10 +95,6 @@ decl_module! {
|
||||
*fm = T::FeeMultiplierUpdate::convert(*fm)
|
||||
});
|
||||
}
|
||||
|
||||
fn on_runtime_upgrade() {
|
||||
migration::on_runtime_upgrade()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +310,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Migration code to update storage.
|
||||
|
||||
use super::*;
|
||||
use frame_support::storage::migration::{put_storage_value, take_storage_value};
|
||||
|
||||
pub fn on_runtime_upgrade() {
|
||||
change_name_balances_to_transaction_payment()
|
||||
}
|
||||
|
||||
// Change the storage name used by this pallet from `Balances` to `TransactionPayment`.
|
||||
//
|
||||
// Since the format of the storage items themselves have not changed, we do not
|
||||
// need to keep track of a storage version. If the runtime does not need to be
|
||||
// upgraded, nothing here will happen anyway.
|
||||
|
||||
fn change_name_balances_to_transaction_payment() {
|
||||
sp_runtime::print("Migrating Transaction Payment.");
|
||||
|
||||
if let Some(next_fee_multiplier) = take_storage_value::<Multiplier>(b"Balances", b"NextFeeMultiplier", &[]) {
|
||||
put_storage_value(b"TransactionPayment", b"NextFeeMultiplier", &[], next_fee_multiplier);
|
||||
}
|
||||
}
|
||||
@@ -200,7 +200,9 @@ decl_storage! {
|
||||
ProposalCount get(fn proposal_count): ProposalIndex;
|
||||
|
||||
/// Proposals that have been made.
|
||||
Proposals get(fn proposals): map hasher(blake2_256) ProposalIndex => Option<Proposal<T::AccountId, BalanceOf<T>>>;
|
||||
Proposals get(fn proposals):
|
||||
map hasher(twox_64_concat) ProposalIndex
|
||||
=> Option<Proposal<T::AccountId, BalanceOf<T>>>;
|
||||
|
||||
/// Proposal indices that have been approved but not yet awarded.
|
||||
Approvals get(fn approvals): Vec<ProposalIndex>;
|
||||
@@ -208,12 +210,13 @@ decl_storage! {
|
||||
/// Tips that are not yet completed. Keyed by the hash of `(reason, who)` from the value.
|
||||
/// This has the insecure enumerable hash function since the key itself is already
|
||||
/// guaranteed to be a secure hash.
|
||||
pub Tips get(fn tips): map hasher(twox_64_concat) T::Hash
|
||||
pub Tips get(fn tips):
|
||||
map hasher(twox_64_concat) T::Hash
|
||||
=> Option<OpenTip<T::AccountId, BalanceOf<T>, T::BlockNumber, T::Hash>>;
|
||||
|
||||
/// Simple preimage lookup from the reason's hash to the original data. Again, has an
|
||||
/// insecure enumerable hash since the key is guaranteed to be the result of a secure hash.
|
||||
pub Reasons get(fn reasons): map hasher(twox_64_concat) T::Hash => Option<Vec<u8>>;
|
||||
pub Reasons get(fn reasons): map hasher(identity) T::Hash => Option<Vec<u8>>;
|
||||
}
|
||||
add_extra_genesis {
|
||||
build(|_config| {
|
||||
@@ -280,8 +283,22 @@ decl_error! {
|
||||
}
|
||||
}
|
||||
|
||||
mod migration {
|
||||
use super::*;
|
||||
pub fn migrate<T: Trait>() {
|
||||
for i in 0..ProposalCount::get() {
|
||||
Proposals::<T>::migrate_key_from_blake(i);
|
||||
}
|
||||
Reasons::<T>::remove_all();
|
||||
}
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn on_runtime_upgrade() {
|
||||
migration::migrate::<T>();
|
||||
}
|
||||
|
||||
/// Fraction of a proposal's value that should be bonded in order to place the proposal.
|
||||
/// An accepted proposal gets these back. A rejected proposal does not.
|
||||
const ProposalBond: Permill = T::ProposalBond::get();
|
||||
@@ -759,7 +776,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -623,7 +623,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -53,10 +53,7 @@ use sp_runtime::{DispatchResult, RuntimeDebug, traits::{
|
||||
StaticLookup, Zero, AtLeast32Bit, MaybeSerializeDeserialize, Convert
|
||||
}};
|
||||
use frame_support::{decl_module, decl_event, decl_storage, decl_error, ensure};
|
||||
use frame_support::traits::{
|
||||
Currency, LockableCurrency, VestingSchedule, WithdrawReason, LockIdentifier, ExistenceRequirement,
|
||||
Get,
|
||||
};
|
||||
use frame_support::traits::{Currency, LockableCurrency, VestingSchedule, WithdrawReason, LockIdentifier, ExistenceRequirement, Get, MigrateAccount};
|
||||
use frame_support::weights::SimpleDispatchInfo;
|
||||
use frame_system::{self as system, ensure_signed};
|
||||
|
||||
@@ -118,7 +115,8 @@ decl_storage! {
|
||||
trait Store for Module<T: Trait> as Vesting {
|
||||
/// Information regarding the vesting of a given account.
|
||||
pub Vesting get(fn vesting):
|
||||
map hasher(blake2_256) T::AccountId => Option<VestingInfo<BalanceOf<T>, T::BlockNumber>>;
|
||||
map hasher(blake2_128_concat) T::AccountId
|
||||
=> Option<VestingInfo<BalanceOf<T>, T::BlockNumber>>;
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(vesting): Vec<(T::AccountId, T::BlockNumber, T::BlockNumber, BalanceOf<T>)>;
|
||||
@@ -256,6 +254,12 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> MigrateAccount<T::AccountId> for Module<T> {
|
||||
fn migrate_account(a: &T::AccountId) {
|
||||
Vesting::<T>::migrate_key_from_blake(a);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// (Re)set or remove the module's currency lock on `who`'s account in accordance with their
|
||||
/// current unvested amount.
|
||||
@@ -384,7 +388,7 @@ mod tests {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
|
||||
@@ -391,7 +391,7 @@ impl frame_system::Trait for Runtime {
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type MigrateAccount = (); type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ decl_module! {
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as TestRuntime {
|
||||
ExtrinsicData: map hasher(blake2_256) u32 => Vec<u8>;
|
||||
ExtrinsicData: map hasher(blake2_128_concat) u32 => Vec<u8>;
|
||||
// The current block number being processed. Set by `execute_block`.
|
||||
Number get(fn number): Option<BlockNumber>;
|
||||
ParentHash get(fn parent_hash): Hash;
|
||||
|
||||
@@ -25,7 +25,7 @@ use jsonrpc_client_transports::RpcError;
|
||||
use codec::{DecodeAll, FullCodec, FullEncode};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use frame_support::storage::generator::{
|
||||
StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue
|
||||
StorageDoubleMap, StorageMap, StorageValue
|
||||
};
|
||||
use sp_storage::{StorageData, StorageKey};
|
||||
use sc_rpc_api::state::StateClient;
|
||||
@@ -63,9 +63,9 @@ use sc_rpc_api::state::StateClient;
|
||||
/// decl_storage! {
|
||||
/// trait Store for Module<T: Trait> as TestRuntime {
|
||||
/// pub LastActionId: u64;
|
||||
/// pub Voxels: map hasher(blake2_256) Loc => Block;
|
||||
/// pub Actions: linked_map hasher(blake2_256) u64 => Loc;
|
||||
/// pub Prefab: double_map hasher(blake2_256) u128, hasher(blake2_256) (i8, i8, i8) => Block;
|
||||
/// pub Voxels: map hasher(blake2_128_concat) Loc => Block;
|
||||
/// pub Actions: map hasher(blake2_128_concat) u64 => Loc;
|
||||
/// pub Prefab: double_map hasher(blake2_128_concat) u128, hasher(blake2_128_concat) (i8, i8, i8) => Block;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@@ -79,7 +79,7 @@ use sc_rpc_api::state::StateClient;
|
||||
/// let q = StorageQuery::map::<Voxels, _>((0, 0, 0));
|
||||
/// let _: Option<Block> = q.get(&cl, None).await?;
|
||||
///
|
||||
/// let q = StorageQuery::linked_map::<Actions, _>(12);
|
||||
/// let q = StorageQuery::map::<Actions, _>(12);
|
||||
/// let _: Option<Loc> = q.get(&cl, None).await?;
|
||||
///
|
||||
/// let q = StorageQuery::double_map::<Prefab, _, _>(3, (0, 0, 0));
|
||||
@@ -111,14 +111,6 @@ impl<V: FullCodec> StorageQuery<V> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a storage query for a value in a StorageLinkedMap.
|
||||
pub fn linked_map<St: StorageLinkedMap<K, V>, K: FullCodec>(key: K) -> Self {
|
||||
Self {
|
||||
key: StorageKey(St::storage_linked_map_final_key(key)),
|
||||
_spook: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a storage query for a value in a StorageDoubleMap.
|
||||
pub fn double_map<St: StorageDoubleMap<K1, K2, V>, K1: FullEncode, K2: FullEncode>(
|
||||
key1: K1,
|
||||
|
||||
Reference in New Issue
Block a user