Skip to content

Commit

Permalink
Add Operations Module
Browse files Browse the repository at this point in the history
  • Loading branch information
balqaasem committed Mar 13, 2024
1 parent 9b135c5 commit f60aec7
Show file tree
Hide file tree
Showing 9 changed files with 1,159 additions and 0 deletions.
48 changes: 48 additions & 0 deletions blockchain/modules/operations/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[package]
name = "module-operations"
version = "0.1.0"
authors.workspace = true
edition.workspace = true
homepage.workspace = true
repository.workspace = true

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

frame-support = { workspace = true }
frame-system = { workspace = true }
pallet-session = { workspace = true }
pallet-balances = { workspace = true }
sp-runtime = { workspace = true }
sp-core = { workspace = true }

[dev-dependencies]
sp-io = { workspace = true }
pallet-staking = { workspace = true }
pallet-timestamp = { workspace = true }
frame-election-provider-support = { workspace = true }

[features]
default = ["std"]
std = [
"parity-scale-codec/std",
"scale-info/std",
"log/std",

"frame-support/std",
"frame-system/std",
"pallet-session/std",
"pallet-balances/std",
"pallet-staking/std",
"pallet-timestamp/std",
"frame-election-provider-support/std",
"sp-runtime/std",
"sp-core/std",

]

try-runtime = [
"frame-support/try-runtime",
]
18 changes: 18 additions & 0 deletions blockchain/modules/operations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Operations Module

General ChainOps extrinsic that are used in chain maintenance activities.

## fix_accounts_consumers_underflow

An account can have an underflow of a `consumers` counter.
Account categories that are impacted by this issue depends on a chain runtime,
but specifically for AlephNode runtime are as follows:

* `consumers` == 0, `reserved` > 0
* `consumers` == 1, `balances.Locks` contain an entry with `id` == `vesting`
* `consumers` == 2, `balances.Locks` contain an entry with `id` == `staking`
* `consumers` == 3, `balances.Locks` contain entries with `id` == `staking`
and account id is in `session.nextKeys`

`fix_accounts_consumers_underflow` checks if the account falls into one of above
categories, and increase its `consumers` counter.
55 changes: 55 additions & 0 deletions blockchain/modules/operations/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# To-Do List

This list contains all TODOs in the Repo


<!-- TOC -->
- [To-Do List](#to-do-list)
- [1. Guidelines](#1-guidelines)
- [2. Contribution](#2-contribution)
- [3. Lists](#3-lists)
- [4. Tasks](#4-tasks)
<!-- /TOC -->

## 1. Guidelines

Note: Before you write a ToDo in this repo, please read the below guidelines carefully.

Whenever you write a ToDo, you need to follow this standard syntax

```rust
//TODO:[file_name:task_number] - task_details
```

for example:

```rust
//TODO:[TODO.md:0] - Add Todo Guidelines
```

Note > the `//TODO:[filename:task_number] - ` is what we call the `task_prefix`.

Whenever adding/writing a Task/ToDo, you need to describe the task on this list. Whenever you write a TODO in any file, add a reference to it here. Please make sure the task reference here is titled correctly and as detailed as possible\.

Whenever you `complete` a task/TODO from any file, please tick/complete its reference here and make sure you do it in the same `commit` that completes the task.

Whenever a task is cancelled (discontinued or not needed for w/e reason), please note in the details why it is cancelled, make sure you do it in the same `commit` that removes/cancels the TODO, and add this `-C` as a suffix to its `file_name` in the list here, for example:

```rust
//TODO:[TODO.md-C:0] - Add Todo Guidelines
```

## 2. Contribution

You can contribute to this list by completing tasks or by adding tasks(TODOs) that are currently in the repo but not on the list. You can also contribute by updating old tasks to the new Standard.

## 3. Lists

Each module/crate has its own `TODO.md`.

## 4. Tasks

These tasks are just for this file specifically.

- [x] [[TODO.md:0] - Add TODO.md File](TODO.md): Add a TODO.md file to organise TODOs in the repo.
- [x] [[TODO.md:1] - Add a `task_title`](/TODO.md/#tasks): Adda `task_title`.
98 changes: 98 additions & 0 deletions blockchain/modules/operations/src/impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// ุจูุณู’ู…ู ุงู„ู„ูŽู‘ู‡ู ุงู„ุฑูŽู‘ุญู’ู…ูŽู†ู ุงู„ุฑูŽู‘ุญููŠู…

// This file is part of Setheum.

// Copyright (C) 2019-Present Setheum Labs.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program 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.

// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.

//! The Operations Module.
#![allow(clippy::nonminimal_bool)]

use frame_support::{
dispatch::DispatchResultWithPostInfo, pallet_prelude::Get, traits::LockIdentifier,
WeakBoundedVec,
};
use pallet_balances::BalanceLock;
use parity_scale_codec::Encode;
use sp_core::hexdisplay::HexDisplay;
use sp_runtime::DispatchError;

use crate::{
pallet::{Config, Event, Pallet},
traits::{AccountInfoProvider, BalancesProvider, NextKeysSessionProvider},
LOG_TARGET, STAKING_ID, VESTING_ID,
};

impl<T: Config> Pallet<T> {
/// Checks if account has an underflow of `consumers` counter. In such case, it increments
/// it by one.
pub fn fix_underflow_consumer_counter(who: T::AccountId) -> DispatchResultWithPostInfo {
let mut weight = T::DbWeight::get().reads(1);
let consumers = T::AccountInfoProvider::get_consumers(&who);

weight += T::DbWeight::get().reads(1);
if Self::no_consumers_some_reserved(&who, consumers) {
Self::increment_consumers(who)?;
weight += T::DbWeight::get().writes(1);
return Ok(Some(weight).into());
}

weight += T::DbWeight::get().reads(2);
if Self::staker_has_consumers_underflow(&who, consumers) {
Self::increment_consumers(who)?;
weight += T::DbWeight::get().writes(1);
return Ok(Some(weight).into());
}

log::debug!(
target: LOG_TARGET,
"Account {:?} has correct consumer counter, not incrementing",
HexDisplay::from(&who.encode())
);
Ok(Some(weight).into())
}

fn staker_has_consumers_underflow(who: &T::AccountId, consumers: u32) -> bool {
let locks = T::BalancesProvider::locks(who);
let has_vesting_lock = Self::has_lock(&locks, VESTING_ID);
let vester_has_consumers_underflow = consumers == 1 && has_vesting_lock;
let has_staking_lock = Self::has_lock(&locks, STAKING_ID);
let nominator_has_consumers_underflow = consumers == 2 && has_staking_lock;
let has_next_session_keys = T::NextKeysSessionProvider::has_next_session_keys(who);
let validator_has_consumers_underflow =
consumers == 3 && has_staking_lock && has_next_session_keys;
vester_has_consumers_underflow
|| nominator_has_consumers_underflow
|| validator_has_consumers_underflow
}

fn no_consumers_some_reserved(who: &T::AccountId, consumers: u32) -> bool {
let is_reserved_not_zero = T::BalancesProvider::is_reserved_not_zero(who);

consumers == 0 && is_reserved_not_zero
}

fn has_lock<U, V>(locks: &WeakBoundedVec<BalanceLock<U>, V>, id: LockIdentifier) -> bool {
locks.iter().any(|x| x.id == id)
}

fn increment_consumers(who: T::AccountId) -> Result<(), DispatchError> {
frame_system::Pallet::<T>::inc_consumers_without_limit(&who)?;
Self::deposit_event(Event::ConsumersUnderflowFixed { who });
Ok(())
}
}
106 changes: 106 additions & 0 deletions blockchain/modules/operations/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// ุจูุณู’ู…ู ุงู„ู„ูŽู‘ู‡ู ุงู„ุฑูŽู‘ุญู’ู…ูŽู†ู ุงู„ุฑูŽู‘ุญููŠู…

// This file is part of Setheum.

// Copyright (C) 2019-Present Setheum Labs.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program 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.

// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.

//! The Operations Module.
#![cfg_attr(not(feature = "std"), no_std)]
#![doc = include_str!("../README.md")]

extern crate core;

mod impls;
mod traits;

#[cfg(test)]
mod tests;

use frame_support::traits::{LockIdentifier, StorageVersion};

const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
pub const LOG_TARGET: &str = "pallet-operations";
// harcoding as those consts are not public in substrate
pub const STAKING_ID: LockIdentifier = *b"staking ";
pub const VESTING_ID: LockIdentifier = *b"vesting ";

pub use pallet::*;

#[frame_support::pallet]
#[pallet_doc("../README.md")]
pub mod pallet {
use frame_support::{pallet_prelude::*, weights::constants::WEIGHT_REF_TIME_PER_MILLIS};
use frame_system::{ensure_signed, pallet_prelude::OriginFor};

use crate::{
traits::{AccountInfoProvider, BalancesProvider, NextKeysSessionProvider},
STORAGE_VERSION,
};

#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Something that provides information about an account's consumers counter
type AccountInfoProvider: AccountInfoProvider<AccountId = Self::AccountId, RefCount = u32>;
type BalancesProvider: BalancesProvider<AccountId = Self::AccountId>;
type NextKeysSessionProvider: NextKeysSessionProvider<AccountId = Self::AccountId>;
}

#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// An account has fixed its consumers counter underflow
ConsumersUnderflowFixed { who: T::AccountId },
}

#[pallet::call]
impl<T: Config> Pallet<T> {
/// An account can have an underflow of a `consumers` counter.
/// Account categories that are impacted by this issue depends on a chain runtime,
/// but specifically for AlephNode runtime are as follows:
/// * `consumers` == 0, `reserved` > 0
/// * `consumers` == 1, `balances.Locks` contain an entry with `id` == `vesting`
/// * `consumers` == 2, `balances.Locks` contain an entry with `id` == `staking`
/// * `consumers` == 3, `balances.Locks` contain entries with `id` == `staking`
/// and account id is in `session.nextKeys`
///
/// `fix_accounts_consumers_underflow` checks if the account falls into one of above
/// categories, and increase its `consumers` counter.
///
/// - `origin`: Must be `Signed`.
/// - `who`: An account to be fixed
///
#[pallet::call_index(0)]
#[pallet::weight(
Weight::from_parts(WEIGHT_REF_TIME_PER_MILLIS.saturating_mul(8), 0)
)]
pub fn fix_accounts_consumers_underflow(
origin: OriginFor<T>,
who: T::AccountId,
) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
Self::fix_underflow_consumer_counter(who)?;
Ok(().into())
}
}
}
24 changes: 24 additions & 0 deletions blockchain/modules/operations/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// ุจูุณู’ู…ู ุงู„ู„ูŽู‘ู‡ู ุงู„ุฑูŽู‘ุญู’ู…ูŽู†ู ุงู„ุฑูŽู‘ุญููŠู…

// This file is part of Setheum.

// Copyright (C) 2019-Present Setheum Labs.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program 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.

// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.

//! Tests for the Operations Module.
mod setup;
mod suite;
Loading

0 comments on commit f60aec7

Please sign in to comment.