Skip to content

Commit

Permalink
[AHM] Vesting (#575)
Browse files Browse the repository at this point in the history
Merging into the AHM working branch. Depends on
#579

# Pallet Vesting

Pallet vesting has one storage map to hold the vesting schedules and one
storage value to track the
current version of the pallet. The version can be easily migrated, but
for the schedules it is a bit difficult.

## Storage: Vesting

The vesting schedules are already measured in relay blocks, as can be
seen

[here](https://github.com/polkadot-fellows/runtimes/blob/b613b54d94af5f4702533a56c6260651a14bdccb/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs#L297).
This means that we can just integrate the existing schedules. The only
possibly issue is when there
are lots of pre-existing schedules. The maximal number of schedules is
28; both on Relay and AH.
We cannot use the merge functionality of the vesting pallet since that
can be used as an attack
vector: anyone can send 28 vested transfers with very large unlock
duration and low amount to force
all other schedules to adapt this long unlock period. This would reduce
the rewards per block, which
is bad.  
For now, we are writing all colliding AH schedules into a storage item
for manual inspection later.
It could still happen that unmalicious users will have more than 28
schedules, but as nobody has
used the vested transfers on AH yet.

Q: Maybe we should disable vested transfers with the next runtime
upgrade on AH.

## Storage: StorageVersion

The vesting pallet is not using the proper FRAME version tracking;
rather, it tracks its version in
the `StorageVersion` value. It does this incorrectly though, with Asset
Hub reporting version 0
instead of 1. We ignore and correct this by writing 1 to the storage.


## User Impact

This affects users that have vesting schedules on the Relay chain or on
Asset Hub. There exists a
risk that the number of total schedules exceeds 28, which means that
they will not fit into the
storage anymore.  

We then prioritize the schedules from AH and pause and stash all
schedules that do not fit (up to
28).


- [x] Does not require a CHANGELOG entry

---------

Signed-off-by: Oliver Tale-Yazdi <[email protected]>
  • Loading branch information
ggwpez authored Feb 11, 2025
1 parent 7b84b27 commit daf6723
Show file tree
Hide file tree
Showing 10 changed files with 440 additions and 18 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pallets/ah-migrator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pallet-referenda = { workspace = true }
pallet-scheduler = { workspace = true }
pallet-staking = { workspace = true }
pallet-state-trie-migration = { workspace = true }
pallet-vesting = { workspace = true }
pallet-treasury = { workspace = true }
polkadot-parachain-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
Expand Down Expand Up @@ -69,6 +70,7 @@ std = [
"pallet-staking/std",
"pallet-state-trie-migration/std",
"pallet-treasury/std",
"pallet-vesting/std",
"polkadot-parachain-primitives/std",
"polkadot-runtime-common/std",
"runtime-parachains/std",
Expand Down Expand Up @@ -101,6 +103,7 @@ runtime-benchmarks = [
"pallet-staking/runtime-benchmarks",
"pallet-state-trie-migration/runtime-benchmarks",
"pallet-treasury/runtime-benchmarks",
"pallet-vesting/runtime-benchmarks",
"polkadot-parachain-primitives/runtime-benchmarks",
"polkadot-runtime-common/runtime-benchmarks",
"runtime-parachains/runtime-benchmarks",
Expand All @@ -126,6 +129,7 @@ try-runtime = [
"pallet-staking/try-runtime",
"pallet-state-trie-migration/try-runtime",
"pallet-treasury/try-runtime",
"pallet-vesting/try-runtime",
"polkadot-runtime-common/try-runtime",
"runtime-parachains/try-runtime",
"sp-runtime/try-runtime",
Expand Down
118 changes: 118 additions & 0 deletions pallets/ah-migrator/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! To run these benchmarks, you will need a modified version of `frame-omni-bencher` that can load
//! snapshots of the relay and asset hub. You can find it on branch `oty-ahm-omni-bencher` of the
//! SDK. Install it with
//! `cargo install --path substrate/utils/frame/omni-bencher --profile production`
//!
//! ```bash
//! frame-omni-bencher v1 benchmark pallet --runtime=target/release/wbuild/asset-hub-polkadot-runtime/asset_hub_polkadot_runtime.wasm --pallet "pallet-ah-migrator" --extrinsic "" --snap=ah-polkadot.snap --rc-snap=polkadot.snap
//! ```
use crate::*;
use core::str::FromStr;
use cumulus_primitives_core::{AggregateMessageOrigin, InboundDownwardMessage};
use frame_benchmarking::v2::*;
use frame_support::{traits::EnqueueMessage, weights::WeightMeter};
use frame_system::RawOrigin;
use pallet_rc_migrator::types::PalletMigration;
use xcm::VersionedXcm;

#[benchmarks(where T: pallet_balances::Config)]
mod benchmarks {
use super::*;

#[benchmark]
fn receive_multisigs() {
verify_snapshot::<T>();
let (messages, _cursor) = relay_snapshot(|| {
unwrap_no_debug(pallet_rc_migrator::multisig::MultisigMigrator::<T>::migrate_out_many(
None,
&mut WeightMeter::new(),
))
});

#[extrinsic_call]
_(RawOrigin::Root, messages);

// TODO assert event
}

#[benchmark]
fn receive_nom_pools_messages_pool_members() {
verify_snapshot::<T>();
let (messages, _cursor) = relay_snapshot(|| {
unwrap_no_debug(pallet_rc_migrator::staking::nom_pools::NomPoolsMigrator::<T>::migrate_many(
None,
&mut WeightMeter::new(),
))
});

#[extrinsic_call]
_(RawOrigin::Root, messages);

// TODO assert event
}
}

/// Unwrap something that does not implement Debug. Otherwise we would need to require
/// `pallet_rc_migrator::Config` on out runtime `T`.
pub fn unwrap_no_debug<T, E>(result: Result<T, E>) -> T {
match result {
Ok(t) => t,
Err(_) => panic!("unwrap_no_debug"),
}
}

/// Check that Oliver's account has some balance on AH and Relay.
///
/// This serves as sanity check that the snapshots were loaded correctly.
fn verify_snapshot<T: Config>() {
let raw_acc: [u8; 32] =
hex::decode("6c9e3102dd2c24274667d416e07570ebce6f20ab80ee3fc9917bf4a7568b8fd2")
.unwrap()
.try_into()
.unwrap();
let acc = AccountId32::from(raw_acc);
frame_system::Pallet::<T>::reset_events();

// Sanity check that this is the right account
let ah_acc = frame_system::Account::<T>::get(&acc);
if ah_acc.data.free == 0 {
panic!("No or broken snapshot: account does not have any balance");
}

let key = frame_system::Account::<T>::hashed_key_for(&acc);
let raw_acc = relay_snapshot(|| {
frame_support::storage::unhashed::get::<
pallet_balances::AccountData<<T as pallet_balances::Config>::Balance>,
>(key.as_ref())
}).unwrap();

if raw_acc.free == 0 {
panic!("No or broken snapshot: account does not have any balance");
}
}

/// Read something from the relay chain snapshot instead of the asset hub one.
fn relay_snapshot<R, F: FnOnce() -> R>(f: F) -> R {
sp_io::storage::get(b"relay_chain_enable");
let result = f();
sp_io::storage::get(b"relay_chain_disable");
result
}
50 changes: 41 additions & 9 deletions pallets/ah-migrator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub mod referenda;
pub mod scheduler;
pub mod staking;
pub mod types;
pub mod vesting;

pub use pallet::*;
pub use pallet_rc_migrator::types::ZeroWeightOr;
Expand Down Expand Up @@ -73,6 +74,7 @@ use pallet_rc_migrator::{
fast_unstake::{FastUnstakeMigrator, RcFastUnstakeMessage},
nom_pools::*,
},
vesting::RcVestingSchedule,
};
use pallet_referenda::TrackIdOf;
use polkadot_runtime_common::claims as pallet_claims;
Expand Down Expand Up @@ -100,6 +102,7 @@ pub enum PalletEventName {
Indices,
FastUnstake,
BagsList,
Vesting,
Bounties,
}

Expand All @@ -122,6 +125,7 @@ pub mod pallet {
+ pallet_fast_unstake::Config
+ pallet_bags_list::Config<pallet_bags_list::Instance1>
+ pallet_scheduler::Config
+ pallet_vesting::Config
+ pallet_indices::Config
+ pallet_conviction_voting::Config
+ pallet_bounties::Config
Expand Down Expand Up @@ -195,6 +199,9 @@ pub mod pallet {
FailedToConvertCall,
/// Failed to bound a call.
FailedToBoundCall,
/// Failed to integrate a vesting schedule.
FailedToIntegrateVestingSchedule,
Unreachable,
}

#[pallet::event]
Expand Down Expand Up @@ -344,6 +351,21 @@ pub mod pallet {
/// How many scheduler messages failed to integrate.
count_bad: u32,
},
/// Should not happen. Manual intervention by the Fellowship required.
///
/// Can happen when existing AH and incoming RC vesting schedules have more combined
/// entries than allowed. This triggers the merging logic which has henceforth failed
/// with the given inner pallet-vesting error.
FailedToMergeVestingSchedules {
/// The account that failed to merge the schedules.
who: AccountId32,
/// The first schedule index that failed to merge.
schedule1: u32,
/// The second schedule index that failed to merge.
schedule2: u32,
/// The index of the pallet-vesting error that occurred.
pallet_vesting_error_index: Option<u8>,
},
ConvictionVotingMessagesReceived {
/// How many conviction voting messages are in the batch.
count: u32,
Expand Down Expand Up @@ -465,6 +487,16 @@ pub mod pallet {
}

#[pallet::call_index(8)]
pub fn receive_vesting_schedules(
origin: OriginFor<T>,
schedules: Vec<RcVestingSchedule<T>>,
) -> DispatchResult {
ensure_root(origin)?;

Self::do_receive_vesting_schedules(schedules).map_err(Into::into)
}

#[pallet::call_index(9)]
pub fn receive_fast_unstake_messages(
origin: OriginFor<T>,
messages: Vec<RcFastUnstakeMessage<T>>,
Expand All @@ -475,7 +507,7 @@ pub mod pallet {
}

/// Receive referendum counts, deciding counts, votes for the track queue.
#[pallet::call_index(9)]
#[pallet::call_index(10)]
pub fn receive_referenda_values(
origin: OriginFor<T>,
referendum_count: u32,
Expand All @@ -491,7 +523,7 @@ pub mod pallet {
}

/// Receive referendums from the Relay Chain.
#[pallet::call_index(10)]
#[pallet::call_index(11)]
pub fn receive_referendums(
origin: OriginFor<T>,
referendums: Vec<(u32, RcReferendumInfoOf<T, ()>)>,
Expand All @@ -501,7 +533,7 @@ pub mod pallet {
Self::do_receive_referendums(referendums).map_err(Into::into)
}

#[pallet::call_index(11)]
#[pallet::call_index(12)]
pub fn receive_claims(
origin: OriginFor<T>,
messages: Vec<RcClaimsMessageOf<T>>,
Expand All @@ -511,7 +543,7 @@ pub mod pallet {
Self::do_receive_claims(messages).map_err(Into::into)
}

#[pallet::call_index(12)]
#[pallet::call_index(13)]
pub fn receive_bags_list_messages(
origin: OriginFor<T>,
messages: Vec<RcBagsListMessage<T>>,
Expand All @@ -521,7 +553,7 @@ pub mod pallet {
Self::do_receive_bags_list_messages(messages).map_err(Into::into)
}

#[pallet::call_index(13)]
#[pallet::call_index(14)]
pub fn receive_scheduler_messages(
origin: OriginFor<T>,
messages: Vec<scheduler::RcSchedulerMessageOf<T>>,
Expand All @@ -531,7 +563,7 @@ pub mod pallet {
Self::do_receive_scheduler_messages(messages).map_err(Into::into)
}

#[pallet::call_index(14)]
#[pallet::call_index(15)]
pub fn receive_indices(
origin: OriginFor<T>,
indices: Vec<RcIndicesIndexOf<T>>,
Expand All @@ -541,7 +573,7 @@ pub mod pallet {
Self::do_receive_indices(indices).map_err(Into::into)
}

#[pallet::call_index(15)]
#[pallet::call_index(16)]
pub fn receive_conviction_voting_messages(
origin: OriginFor<T>,
messages: Vec<RcConvictionVotingMessageOf<T>>,
Expand All @@ -551,7 +583,7 @@ pub mod pallet {
Self::do_receive_conviction_voting_messages(messages).map_err(Into::into)
}

#[pallet::call_index(16)]
#[pallet::call_index(17)]
pub fn receive_bounties_messages(
origin: OriginFor<T>,
messages: Vec<pallet_rc_migrator::bounties::RcBountiesMessageOf<T>>,
Expand All @@ -561,7 +593,7 @@ pub mod pallet {
Self::do_receive_bounties_messages(messages).map_err(Into::into)
}

#[pallet::call_index(17)]
#[pallet::call_index(18)]
pub fn receive_asset_rates(
origin: OriginFor<T>,
rates: Vec<(<T as pallet_asset_rate::Config>::AssetKind, FixedU128)>,
Expand Down
Loading

0 comments on commit daf6723

Please sign in to comment.