Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pallet_external_validators, remove ValidatorManager #722

Merged
merged 42 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5811024
Add pallet_external_validators, remove ValidatorManager
tmpolaczyk Oct 15, 2024
8fa98f0
Rename extrinsics and add tests
tmpolaczyk Oct 17, 2024
86cb5cd
Fix tests
tmpolaczyk Oct 17, 2024
1c36e24
Merge remote-tracking branch 'origin/master' into tomasz-external-val…
tmpolaczyk Oct 17, 2024
57f2949
Fix benchmarks
tmpolaczyk Oct 17, 2024
81381cd
typescript-api
tmpolaczyk Oct 17, 2024
b7b4e51
Add traits
tmpolaczyk Oct 18, 2024
b3d8c5b
Merge remote-tracking branch 'origin/master' into tomasz-external-val…
tmpolaczyk Oct 18, 2024
abec023
Merge remote-tracking branch 'origin/master' into tomasz-external-val…
tmpolaczyk Oct 21, 2024
06ac945
Some PR comments
tmpolaczyk Oct 21, 2024
6edc6f4
Change era every n sessions
tmpolaczyk Oct 21, 2024
71ae9d1
WIP integration test and hooks
tmpolaczyk Oct 22, 2024
5f5c819
Merge remote-tracking branch 'origin/master' into tomasz-external-val…
tmpolaczyk Oct 22, 2024
61c3fc9
Benchmark new extrinsics
tmpolaczyk Oct 22, 2024
08965a8
Migrate queued keys instead of current validators
tmpolaczyk Oct 22, 2024
bc61273
typescript-api
tmpolaczyk Oct 22, 2024
e2fe742
Add era index to hook
tmpolaczyk Oct 22, 2024
99791ec
impl for tuples
tmpolaczyk Oct 22, 2024
7cde793
Make tests pass
tmpolaczyk Oct 22, 2024
09cfcec
Add integration tests
tmpolaczyk Oct 23, 2024
c6cf701
current era and active era
tmpolaczyk Oct 23, 2024
a49f749
typescript-api
tmpolaczyk Oct 23, 2024
1187bc4
Add one typescript test
tmpolaczyk Oct 24, 2024
9614ac8
console log
tmpolaczyk Oct 24, 2024
adc79c2
Merge remote-tracking branch 'origin/master' into tomasz-external-val…
tmpolaczyk Oct 25, 2024
6591098
run_to_block not working
tmpolaczyk Oct 25, 2024
3375e5d
Unit tests still not working
tmpolaczyk Oct 28, 2024
40fdf84
Fix unit tests
tmpolaczyk Oct 28, 2024
aaadb2c
Start era 0 in session 0
tmpolaczyk Oct 29, 2024
8cd3a41
Merge remote-tracking branch 'origin/master' into tomasz-external-val…
tmpolaczyk Oct 29, 2024
12646cf
Rewrite pallet logic, copy it from pallet_staking
tmpolaczyk Oct 29, 2024
f7a57e5
Test hook calls
tmpolaczyk Oct 29, 2024
4f5ef3c
typescript-api
tmpolaczyk Oct 29, 2024
eae263f
Add era session start to trait
tmpolaczyk Oct 29, 2024
50f8237
pr comments
tmpolaczyk Oct 30, 2024
485ce85
typescript-api
tmpolaczyk Oct 30, 2024
55cdaaa
Merge remote-tracking branch 'origin/master' into tomasz-external-val…
tmpolaczyk Oct 30, 2024
5cdac08
typescript-api
tmpolaczyk Oct 30, 2024
2b9e475
Test migration
tmpolaczyk Oct 31, 2024
efc69f1
api
girazoki Oct 31, 2024
5469dff
Some more comments
tmpolaczyk Oct 31, 2024
47721db
Merge branch 'master' into tomasz-external-validators
tmpolaczyk Oct 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pallet-collator-assignment-runtime-api = { path = "pallets/collator-assignment/r
pallet-configuration = { path = "pallets/configuration", default-features = false }
pallet-data-preservers = { path = "pallets/data-preservers", default-features = false }
pallet-data-preservers-runtime-api = { path = "pallets/data-preservers/runtime-api", default-features = false }
pallet-external-validators = { path = "pallets/external-validators", default-features = false }
girazoki marked this conversation as resolved.
Show resolved Hide resolved
pallet-inflation-rewards = { path = "pallets/inflation-rewards", default-features = false }
pallet-initializer = { path = "pallets/initializer", default-features = false }
pallet-invulnerables = { path = "pallets/invulnerables", default-features = false }
Expand Down
77 changes: 77 additions & 0 deletions pallets/external-validators/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
[package]
name = "pallet-external-validators"
authors = { workspace = true }
description = "Simple pallet to store external validators."
edition = "2021"
license = "GPL-3.0-only"
version = "0.1.0"

[package.metadata.docs.rs]
targets = [ "x86_64-unknown-linux-gnu" ]

[lints]
workspace = true

[dependencies]
log = { workspace = true }
parity-scale-codec = { workspace = true }
rand = { workspace = true, optional = true }
scale-info = { workspace = true, features = [ "derive" ] }

frame-support = { workspace = true }
frame-system = { workspace = true }
sp-runtime = { workspace = true }
sp-staking = { workspace = true }
sp-std = { workspace = true }
tp-traits = { workspace = true }

frame-benchmarking = { workspace = true }

pallet-balances = { workspace = true, optional = true }
tmpolaczyk marked this conversation as resolved.
Show resolved Hide resolved
pallet-session = { workspace = true, features = [ "historical" ] }

[dev-dependencies]
pallet-timestamp = { workspace = true }
sp-core = { workspace = true }
sp-io = { workspace = true }

[features]
default = [ "std" ]
std = [
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"log/std",
"pallet-balances/std",
"pallet-session/std",
"pallet-timestamp/std",
"parity-scale-codec/std",
"rand?/std",
"scale-info/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-staking/std",
"sp-std/std",
"tp-traits/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"rand",
"sp-runtime/runtime-benchmarks",
"sp-staking/runtime-benchmarks",
"tp-traits/runtime-benchmarks",
]

try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-balances?/try-runtime",
"pallet-session/try-runtime",
"pallet-timestamp/try-runtime",
"sp-runtime/try-runtime",
]
237 changes: 237 additions & 0 deletions pallets/external-validators/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// Copyright (C) Moondance Labs Ltd.
// This file is part of Tanssi.

// Tanssi 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.

// Tanssi 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 Tanssi. If not, see <http://www.gnu.org/licenses/>

//! Benchmarking setup for pallet_external_validators

use super::*;

#[allow(unused)]
use crate::Pallet as ExternalValidators;
use {
frame_benchmarking::{account, v2::*, BenchmarkError},
frame_support::{
pallet_prelude::*,
traits::{tokens::fungible::Balanced, Currency, EnsureOrigin, Get},
},
frame_system::{EventRecord, RawOrigin},
pallet_session::{self as session, SessionManager},
sp_runtime::traits::{AtLeast32BitUnsigned, Convert},
sp_std::prelude::*,
tp_traits::DistributeRewards,
};
const SEED: u32 = 0;

fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
let events = frame_system::Pallet::<T>::events();
let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
// compare to the last event record
let EventRecord { event, .. } = &events[events.len() - 1];
assert_eq!(event, &system_event);
}

fn create_funded_user<T: Config + pallet_balances::Config>(
string: &'static str,
n: u32,
balance_factor: u32,
) -> T::AccountId {
let user = account(string, n, SEED);
let balance = <pallet_balances::Pallet<T> as Currency<T::AccountId>>::minimum_balance()
* balance_factor.into();
let _ = <pallet_balances::Pallet<T> as Currency<T::AccountId>>::make_free_balance_be(
&user, balance,
);
user
}

fn keys<T: Config + session::Config>(c: u32) -> <T as session::Config>::Keys {
use rand::{RngCore, SeedableRng};

let keys = {
let mut keys = [0u8; 256];

if c > 0 {
let mut rng = rand::rngs::StdRng::seed_from_u64(u64::from(c));
rng.fill_bytes(&mut keys);
}

keys
};

Decode::decode(&mut &keys[..]).unwrap()
}

fn invulnerable<T: Config + session::Config + pallet_balances::Config>(
c: u32,
) -> (
T::AccountId,
<T as Config>::ValidatorId,
<T as session::Config>::Keys,
) {
let funded_user = create_funded_user::<T>("candidate", c, 100);
let collator_id = <T as Config>::ValidatorIdOf::convert(funded_user.clone())
.expect("Converstion of account id of collator id failed.");
(funded_user, collator_id, keys::<T>(c))
}

fn invulnerables<
T: Config + frame_system::Config + pallet_session::Config + pallet_balances::Config,
>(
count: u32,
) -> Vec<(T::AccountId, <T as Config>::ValidatorId)> {
let invulnerables = (0..count).map(|c| invulnerable::<T>(c)).collect::<Vec<_>>();

for (who, _collator_id, keys) in invulnerables.clone() {
<session::Pallet<T>>::set_keys(RawOrigin::Signed(who).into(), keys, Vec::new()).unwrap();
}

invulnerables
.into_iter()
.map(|(who, collator_id, _)| (who, collator_id))
.collect()
}

#[allow(clippy::multiple_bound_locations)]
#[benchmarks(where T: session::Config + pallet_balances::Config)]
mod benchmarks {
use super::*;

#[benchmark]
fn skip_external_validators() -> Result<(), BenchmarkError> {
let origin =
T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;

#[extrinsic_call]
_(origin as T::RuntimeOrigin, true);

Ok(())
}

#[benchmark]
fn add_whitelisted(
b: Linear<1, { T::MaxWhitelistedValidators::get() - 1 }>,
) -> Result<(), BenchmarkError> {
let origin =
T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;

// now we need to fill up invulnerables
let invulnerables = invulnerables::<T>(b);

let (_account_ids, collator_ids): (Vec<T::AccountId>, Vec<<T as Config>::ValidatorId>) =
invulnerables.into_iter().unzip();

let invulnerables: frame_support::BoundedVec<_, T::MaxWhitelistedValidators> =
frame_support::BoundedVec::try_from(collator_ids).unwrap();
<WhitelistedValidators<T>>::put(invulnerables);

let (new_invulnerable, _collator_id, keys) = invulnerable::<T>(b + 1);
<session::Pallet<T>>::set_keys(
RawOrigin::Signed(new_invulnerable.clone()).into(),
keys,
Vec::new(),
)
.unwrap();

#[extrinsic_call]
_(origin as T::RuntimeOrigin, new_invulnerable.clone());

assert_last_event::<T>(
Event::WhitelistedValidatorAdded {
account_id: new_invulnerable,
}
.into(),
);
Ok(())
}

#[benchmark]
fn remove_whitelisted(
b: Linear<{ 1 }, { T::MaxWhitelistedValidators::get() }>,
) -> Result<(), BenchmarkError> {
let origin =
T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let invulnerables = invulnerables::<T>(b);

let (account_ids, collator_ids): (Vec<T::AccountId>, Vec<<T as Config>::ValidatorId>) =
invulnerables.into_iter().unzip();

let invulnerables: frame_support::BoundedVec<_, T::MaxWhitelistedValidators> =
frame_support::BoundedVec::try_from(collator_ids).unwrap();
<WhitelistedValidators<T>>::put(invulnerables);

let to_remove = account_ids.last().unwrap().clone();

#[extrinsic_call]
_(origin as T::RuntimeOrigin, to_remove.clone());

assert_last_event::<T>(
Event::WhitelistedValidatorRemoved {
account_id: to_remove,
}
.into(),
);
Ok(())
}

#[benchmark]
fn force_era() -> Result<(), BenchmarkError> {
let origin =
T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;

#[extrinsic_call]
_(origin as T::RuntimeOrigin, Forcing::ForceNew);

Ok(())
}

// worst case for new session.
#[benchmark]
fn new_session(
r: Linear<1, { T::MaxWhitelistedValidators::get() }>,
) -> Result<(), BenchmarkError> {
// start fresh
WhitelistedValidators::<T>::kill();

let origin =
T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;

frame_system::Pallet::<T>::set_block_number(0u32.into());
// now we need to fill up invulnerables
let invulnerables = invulnerables::<T>(r);

let (account_ids, _collator_ids): (Vec<T::AccountId>, Vec<<T as Config>::ValidatorId>) =
invulnerables.into_iter().unzip();

for account in account_ids {
<ExternalValidators<T>>::add_whitelisted(origin.clone(), account)
.expect("add whitelisted failed");
}

let new_era_session = T::SessionsPerEra::get();

#[block]
{
<ExternalValidators<T> as SessionManager<_>>::new_session(new_era_session);
}

Ok(())
}

impl_benchmark_test_suite!(
ExternalValidators,
crate::mock::new_test_ext(),
crate::mock::Test,
);
}
Loading
Loading