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

IT: rewards to generic framework. Remove previous non-generic IT utilities #1805

Merged
merged 4 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion ci/run-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ case $TARGET in
;;

test-integration)
cargo test --release --package runtime-integration-tests --features fast-runtime
cargo test --release --package runtime-integration-tests
;;

lint-fmt)
Expand Down
8 changes: 4 additions & 4 deletions runtime/altair/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
GAS_LIMIT_STORAGE_GROWTH_RATIO, WEIGHT_PER_GAS,
},
fees::{DealWithFees, FeeToTreasury, WeightToFee},
gateway,
gateway, instances,
liquidity_pools::LiquidityPoolsMessage,
oracle::{
Feeder, OracleConverterBridge, OracleRatioProvider, OracleRatioProviderLocalAssetExtension,
Expand Down Expand Up @@ -1208,7 +1208,7 @@
pub const RewardCurrency: CurrencyId = CurrencyId::Native;
}

impl pallet_rewards::Config<pallet_rewards::Instance1> for Runtime {
impl pallet_rewards::Config<instances::BlockRewards> for Runtime {
type Currency = Tokens;
type CurrencyId = CurrencyId;
type GroupId = u32;
Expand Down Expand Up @@ -2309,14 +2309,14 @@
impl runtime_common::apis::RewardsApi<Block, AccountId, Balance, CurrencyId> for Runtime {
fn list_currencies(domain: runtime_common::apis::RewardDomain, account_id: AccountId) -> Vec<CurrencyId> {
match domain {
runtime_common::apis::RewardDomain::Block => pallet_rewards::Pallet::<Runtime, pallet_rewards::Instance1>::list_currencies(&account_id),
runtime_common::apis::RewardDomain::Block => pallet_rewards::Pallet::<Runtime, instances::BlockRewards>::list_currencies(&account_id),

Check warning on line 2312 in runtime/altair/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

runtime/altair/src/lib.rs#L2312

Added line #L2312 was not covered by tests
_ => vec![],
}
}

fn compute_reward(domain: runtime_common::apis::RewardDomain, currency_id: CurrencyId, account_id: AccountId) -> Option<Balance> {
match domain {
runtime_common::apis::RewardDomain::Block => <pallet_rewards::Pallet::<Runtime, pallet_rewards::Instance1> as cfg_traits::rewards::AccountRewards<AccountId>>::compute_reward(currency_id, &account_id).ok(),
runtime_common::apis::RewardDomain::Block => <pallet_rewards::Pallet::<Runtime, instances::BlockRewards> as cfg_traits::rewards::AccountRewards<AccountId>>::compute_reward(currency_id, &account_id).ok(),

Check warning on line 2319 in runtime/altair/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

runtime/altair/src/lib.rs#L2319

Added line #L2319 was not covered by tests
_ => None,
}
}
Expand Down
8 changes: 4 additions & 4 deletions runtime/centrifuge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
GAS_LIMIT_STORAGE_GROWTH_RATIO, WEIGHT_PER_GAS,
},
fees::{DealWithFees, FeeToTreasury, WeightToFee},
gateway,
gateway, instances,
liquidity_pools::LiquidityPoolsMessage,
oracle::{
Feeder, OracleConverterBridge, OracleRatioProvider, OracleRatioProviderLocalAssetExtension,
Expand Down Expand Up @@ -1222,7 +1222,7 @@
pub const RewardCurrency: CurrencyId = CurrencyId::Native;
}

impl pallet_rewards::Config<pallet_rewards::Instance1> for Runtime {
impl pallet_rewards::Config<instances::BlockRewards> for Runtime {
type Currency = Tokens;
type CurrencyId = CurrencyId;
type GroupId = u32;
Expand Down Expand Up @@ -2357,14 +2357,14 @@
impl runtime_common::apis::RewardsApi<Block, AccountId, Balance, CurrencyId> for Runtime {
fn list_currencies(domain: runtime_common::apis::RewardDomain, account_id: AccountId) -> Vec<CurrencyId> {
match domain {
runtime_common::apis::RewardDomain::Block => pallet_rewards::Pallet::<Runtime, pallet_rewards::Instance1>::list_currencies(&account_id),
runtime_common::apis::RewardDomain::Block => pallet_rewards::Pallet::<Runtime, instances::BlockRewards>::list_currencies(&account_id),

Check warning on line 2360 in runtime/centrifuge/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

runtime/centrifuge/src/lib.rs#L2360

Added line #L2360 was not covered by tests
_ => vec![],
}
}

fn compute_reward(domain: runtime_common::apis::RewardDomain, currency_id: CurrencyId, account_id: AccountId) -> Option<Balance> {
match domain {
runtime_common::apis::RewardDomain::Block => <pallet_rewards::Pallet::<Runtime, pallet_rewards::Instance1> as cfg_traits::rewards::AccountRewards<AccountId>>::compute_reward(currency_id, &account_id).ok(),
runtime_common::apis::RewardDomain::Block => <pallet_rewards::Pallet::<Runtime, instances::BlockRewards> as cfg_traits::rewards::AccountRewards<AccountId>>::compute_reward(currency_id, &account_id).ok(),

Check warning on line 2367 in runtime/centrifuge/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

runtime/centrifuge/src/lib.rs#L2367

Added line #L2367 was not covered by tests
_ => None,
}
}
Expand Down
5 changes: 5 additions & 0 deletions runtime/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ use sp_runtime::{
};
use sp_std::marker::PhantomData;

pub mod instances {
/// The rewards associated to block rewards
pub type BlockRewards = pallet_rewards::Instance1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it, thanks!

}

parameter_types! {
/// The native currency identifier of our currency id enum
/// to be used for Get<CurrencyId> types.
Expand Down
8 changes: 4 additions & 4 deletions runtime/development/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
GAS_LIMIT_STORAGE_GROWTH_RATIO, WEIGHT_PER_GAS,
},
fees::{DealWithFees, FeeToTreasury, WeightToFee},
gateway,
gateway, instances,
liquidity_pools::LiquidityPoolsMessage,
oracle::{
Feeder, OracleConverterBridge, OracleRatioProvider, OracleRatioProviderLocalAssetExtension,
Expand Down Expand Up @@ -1707,7 +1707,7 @@
type WeightInfo = ();
}

impl pallet_rewards::Config<pallet_rewards::Instance1> for Runtime {
impl pallet_rewards::Config<instances::BlockRewards> for Runtime {
type Currency = Tokens;
type CurrencyId = CurrencyId;
type GroupId = u32;
Expand Down Expand Up @@ -2396,14 +2396,14 @@
impl runtime_common::apis::RewardsApi<Block, AccountId, Balance, CurrencyId> for Runtime {
fn list_currencies(domain: runtime_common::apis::RewardDomain, account_id: AccountId) -> Vec<CurrencyId> {
match domain {
runtime_common::apis::RewardDomain::Block => pallet_rewards::Pallet::<Runtime, pallet_rewards::Instance1>::list_currencies(&account_id),
runtime_common::apis::RewardDomain::Block => pallet_rewards::Pallet::<Runtime, instances::BlockRewards>::list_currencies(&account_id),

Check warning on line 2399 in runtime/development/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

runtime/development/src/lib.rs#L2399

Added line #L2399 was not covered by tests
runtime_common::apis::RewardDomain::Liquidity => pallet_rewards::Pallet::<Runtime, pallet_rewards::Instance2>::list_currencies(&account_id),
}
}

fn compute_reward(domain: runtime_common::apis::RewardDomain, currency_id: CurrencyId, account_id: AccountId) -> Option<Balance> {
match domain {
runtime_common::apis::RewardDomain::Block => <pallet_rewards::Pallet::<Runtime, pallet_rewards::Instance1> as cfg_traits::rewards::AccountRewards<AccountId>>::compute_reward(currency_id, &account_id).ok(),
runtime_common::apis::RewardDomain::Block => <pallet_rewards::Pallet::<Runtime, instances::BlockRewards> as cfg_traits::rewards::AccountRewards<AccountId>>::compute_reward(currency_id, &account_id).ok(),

Check warning on line 2406 in runtime/development/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

runtime/development/src/lib.rs#L2406

Added line #L2406 was not covered by tests
runtime_common::apis::RewardDomain::Liquidity => <pallet_rewards::Pallet::<Runtime, pallet_rewards::Instance2> as cfg_traits::rewards::AccountRewards<AccountId>>::compute_reward(currency_id, &account_id).ok(),
}
}
Expand Down
4 changes: 2 additions & 2 deletions runtime/integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ pallet-xcm-transactor = { workspace = true, features = ["std"] }
parachain-info = { workspace = true, features = ["std"] }

[features]
# There is no need for integration test to add new features.
# Just add the required ones to default:
default = [
"development-runtime/instant-voting",
]

fast-runtime = ["development-runtime/fast-runtime"]
216 changes: 216 additions & 0 deletions runtime/integration-tests/src/generic/cases/block_rewards.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
use cfg_primitives::{AccountId, CFG};
use cfg_traits::rewards::{AccountRewards, CurrencyGroupChange, DistributedRewards};
use cfg_types::tokens::CurrencyId;
use frame_support::{assert_ok, dispatch::RawOrigin};
use runtime_common::{
apis::{runtime_decl_for_rewards_api::RewardsApiV1, RewardDomain},
instances::BlockRewards,
};
use sp_runtime::traits::{Get, Zero};

use crate::{
generic::{
config::{Runtime, RuntimeKind},
env::Env,
envs::runtime_env::RuntimeEnv,
utils,
utils::{
currency::cfg,
genesis::{self, Genesis},
},
},
utils::accounts::{default_accounts, Keyring},
};

crate::test_for_runtimes!(all, block_rewards_api);
fn block_rewards_api<T: Runtime>() {
Comment on lines +25 to +26
Copy link
Contributor Author

@lemunozm lemunozm Apr 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be really cool having this as a procedural macro:

#[test_for_runtimes(all)]
fn block_rewards_api() {
    // ...
}

I think it should not be complicated, and we can leave this much more legible

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be awesome!

const STAKER: Keyring = Keyring::Alice;

RuntimeEnv::<T>::default().parachain_state_mut(|| {
let group_id = 1u32;
let amount = 100 * CFG;

utils::give_balance::<T>(STAKER.id(), T::ExistentialDeposit::get() + amount);

assert_ok!(pallet_rewards::Pallet::<T, BlockRewards>::attach_currency(
CurrencyId::Native,
group_id,
));

assert_ok!(pallet_rewards::Pallet::<T, BlockRewards>::deposit_stake(
CurrencyId::Native,
&STAKER.id(),
amount,
));

assert_ok!(
pallet_rewards::Pallet::<T, BlockRewards>::distribute_reward(200 * CFG, [group_id])
);

assert_eq!(
T::Api::list_currencies(RewardDomain::Block, STAKER.id()),
vec![CurrencyId::Native]
);

assert_eq!(
T::Api::compute_reward(RewardDomain::Block, CurrencyId::Native, STAKER.id()),
Some(200 * CFG)
)
});
}

crate::test_for_runtimes!(
[development, altair, centrifuge],
collator_list_synchronized
);
fn collator_list_synchronized<T: Runtime>() {
RuntimeEnv::<T>::from_parachain_storage(
Genesis::default()
.add(genesis::balances::<T>(
T::ExistentialDeposit::get() + cfg(100),
))
.add(genesis::invulnerables::<T>(vec![Keyring::Admin]))
.add(genesis::session_keys::<T>())
.add(genesis::block_rewards::<T>(vec![Keyring::Admin]))
.storage(),
)
.parachain_state_mut(|| {
let collators_1 = vec![Keyring::Alice.id(), Keyring::Bob.id()];
let collators_2 = vec![
Keyring::Charlie.id(),
Keyring::Dave.id(),
Keyring::Eve.id(),
Keyring::Ferdie.id(),
];

// altair and centrifuge use collator_allowlist,
// so we need to add the accounts there for them.
if T::KIND == RuntimeKind::Altair || T::KIND == RuntimeKind::Centrifuge {
for account in default_accounts() {
assert_ok!(pallet_collator_allowlist::Pallet::<T>::add(
RawOrigin::Root.into(),
account.id()
));
}
}

// SESSION 0 -> 1;
apply_and_check_session::<T>(1, collators_1.clone(), vec![]);

// SESSION 1 -> 2;
apply_and_check_session::<T>(3, collators_2.clone(), vec![]);

// SESSION 2 -> 3;
apply_and_check_session::<T>(7, vec![], vec![Keyring::Alice.id()]);

assert!(collators_1.iter().all(|c| has_reward::<T>(c)));
assert!(collators_2.iter().all(|c| !has_reward::<T>(c)));

// SESSION 3 -> 4;
apply_and_check_session::<T>(6, vec![], vec![]);

assert!(collators_2.iter().all(|c| has_reward::<T>(c)));
});
}

fn apply_and_check_session<T: Runtime>(
collator_count: u32,
joining: Vec<AccountId>,
leaving: Vec<AccountId>,
) {
for collator in &joining {
pallet_collator_selection::Pallet::<T>::register_as_candidate(
RawOrigin::Signed(collator.clone()).into(),
)
.unwrap();
Comment on lines +122 to +125
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't call this in altair and centrifuge because I get a ValidatorNotRegistered error.

@wischli, do you have any idea why this happens?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terms of pallet_collator_selection::Config, Dev and Altair/Centrifuge differ in the ValidatorRegistration trait implementor: Dev uses Sessions whereas production uses the CollatorAllowlist. Therefore, dev only requires session keys to be set for those addresses, which are registered. Production however requires the accounts to be whitelisted via the pallet_collator_allowlist beforehand. This can done via the add extrinsic of that pallet. This also requires the address to have their session key set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for this answer @wischli, it helps me a lot!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wischli, do you think we should align this for all runtimes? Why we are not using collator allowlist for dev?

cc @mustermeiszer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functionally, it does not make sense to me to add an extra gate to collators for our dev runtime IMO. I suppose the only achievement for us would be that this test works for all runtimes?

I definitely dont want to block your proposal, just questioning the effort.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nono, it's not needed for the test; we can discriminate the behavior per runtime (at least it seems so by now). I asked because development is our way of proving stuff that later will go to centrifuge. So maybe it deserves that the demo also uses collator allowlist. Just a thought!

}

for collator in &leaving {
pallet_collator_selection::Pallet::<T>::leave_intent(
RawOrigin::Signed(collator.clone()).into(),
)
.unwrap();
}

frame_system::Pallet::<T>::reset_events();

pallet_session::Pallet::<T>::rotate_session();
Copy link
Contributor Author

@lemunozm lemunozm Apr 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, I've been playing with this and I've substituted pass_n(Period) to rotate_session(), which maps to the same behavior and allow us get rid of fast-runtime feature in integration-test and simplify a bit the test.

If you have some thoughts against it @wischli , I can revert this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats a perfect replacement and upgrade! Thanks a lot.


/* TODO: pending to fix. Why try_into() fails getting the reward_event::GroupRewarded?

// The event exists in this list:
dbg!(frame_system::Pallet::<T>::events())

// But not in in this list (that is the implementation of find_event()),
// so try_into returns an Err for it.
dbg!(frame_system::Pallet::<T>::events()
.into_iter()
.rev()
.find_map(|record| record.event.try_into().ok())
.flatten());

// But later, if manually I create the event as follows:
let e = T::RuntimeEventExt::from(pallet_rewards::Event::<T, BlockRewards>::GroupRewarded {
group_id: 1,
amount: 2,
});

// And I call try_into(), it works.
let re: pallet_rewards::Event<T, BlockRewards> = e.try_into().ok().unwrap();
*/
Comment on lines +139 to +160
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

99% of developers can not fix this nor saying what's happening here

cc: @cdamian @wischli @mustermeiszer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a solution at hand right now, would have to debug that myself. Can look into that next week, if desired.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks William. My guess is that something weird is happening with the try_into when events contain instances. But I've checked all the places where the instance is set, and we always set the correct one for that pallet. 🤔


/* // Uncomment once fix the above issue
utils::find_event::<T, _, _>(|e| match e {
pallet_rewards::Event::<_, BlockRewards>::GroupRewarded { .. } => Some(true),
_ => None,
})
.unwrap();
*/

utils::find_event::<T, _, _>(|e| match e {
pallet_block_rewards::Event::NewSession { .. } => Some(true),
_ => None,
})
.unwrap();

// Checks post applying new session:

assert_eq!(
pallet_block_rewards::Pallet::<T>::active_session_data().collator_count,
collator_count,
);

if !joining.is_empty() || !leaving.is_empty() {
assert_eq!(
pallet_block_rewards::Pallet::<T>::next_session_changes().collator_count,
Some(collator_count + joining.len() as u32 - leaving.len() as u32)
);
}

let next_collators = pallet_block_rewards::Pallet::<T>::next_session_changes().collators;
assert_eq!(*next_collators.inc, joining);
assert_eq!(*next_collators.out, leaving);

assert!(joining.iter().all(|c| !is_staked::<T>(c)));
assert!(joining.iter().all(|c| is_candidate::<T>(c)));
assert!(leaving.iter().all(|c| is_staked::<T>(c)));
assert!(leaving.iter().all(|c| !is_candidate::<T>(c)));
}

fn has_reward<T: Runtime>(collator: &AccountId) -> bool {
!pallet_rewards::Pallet::<T, BlockRewards>::compute_reward(T::StakeCurrencyId::get(), collator)
.unwrap()
.is_zero()
}

fn is_staked<T: Runtime>(collator: &AccountId) -> bool {
!pallet_rewards::Pallet::<T, BlockRewards>::account_stake(T::StakeCurrencyId::get(), collator)
.is_zero()
}

fn is_candidate<T: Runtime>(collator: &AccountId) -> bool {
pallet_collator_selection::Pallet::<T>::candidates()
.into_iter()
.find(|c| c.who == *collator)
.is_some()
}
Loading
Loading