diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index 9debb5fb8..c440a51d9 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -21,7 +21,7 @@ jobs:
- name: Install latest nightly
uses: actions-rs/toolchain@v1
with:
- toolchain: nightly-2022-12-26
+ toolchain: nightly-2023-07-16
override: true
components: rustfmt
@@ -109,30 +109,3 @@ jobs:
- name: Check
run: cargo check --release
- dkg-liveness:
- concurrency:
- group: dkg-liveness-${{ github.ref }}
- cancel-in-progress: true
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
-
- - name: Install toolchain
- id: toolchain
- uses: actions-rs/toolchain@master
- with:
- profile: minimal
- toolchain: nightly
- target: wasm32-unknown-unknown
-
- - name: Rust Cache
- uses: Swatinem/rust-cache@v1.3.0
-
- - name: Install Protobuf
- run: sudo apt-get install protobuf-compiler
-
- - name: Build
- run: cargo build --release -p tangle-standalone --features integration-tests
-
- - name: DKG Liveness
- run: bash ./scripts/ci-dkg-test.sh
diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml
index 7b38af5bc..32c86f783 100644
--- a/.github/workflows/publish-docker.yml
+++ b/.github/workflows/publish-docker.yml
@@ -31,9 +31,6 @@ jobs:
- binary: tangle-standalone
features: relayer
image_name: tangle-standalone-relayer
- - binary: tangle-standalone
- features: light-client
- image_name: tangle-standalone-light-client
permissions:
contents: read
packages: write
diff --git a/.github/workflows/publish-release-binary.yml b/.github/workflows/publish-release-binary.yml
index 73398a7b3..018057ea7 100644
--- a/.github/workflows/publish-release-binary.yml
+++ b/.github/workflows/publish-release-binary.yml
@@ -61,16 +61,4 @@ jobs:
file: target/release/tangle-standalone
asset_name: tangle-standalone-relayer-linux-amd64
tag: ${{ github.ref }}
- overwrite: true
-
- - name: Build binary with light-client
- run: cargo build --release -p tangle-standalone --locked --features light-client
-
- - name: Upload light-client binaries to release
- uses: svenstaro/upload-release-action@v2
- with:
- repo_token: ${{ secrets.GITHUB_TOKEN }}
- file: target/release/tangle-standalone
- asset_name: tangle-standalone-light-client-linux-amd64
- tag: ${{ github.ref }}
overwrite: true
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 50fda8189..08953c963 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8815,6 +8815,7 @@ dependencies = [
"num_enum 0.5.11",
"pallet-balances",
"pallet-dkg-metadata",
+ "pallet-dkg-proposals",
"pallet-evm",
"pallet-session",
"pallet-staking",
@@ -9007,6 +9008,26 @@ dependencies = [
"sp-std 8.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0)",
]
+[[package]]
+name = "pallet-roles"
+version = "0.5.0"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "hex-literal 0.3.4",
+ "pallet-balances",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "serde_json",
+ "sp-core 21.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0)",
+ "sp-io 23.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0)",
+ "sp-runtime 24.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0)",
+ "sp-std 8.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0)",
+ "tangle-primitives",
+]
+
[[package]]
name = "pallet-scheduler"
version = "4.0.0-dev"
@@ -15343,7 +15364,10 @@ version = "0.5.0"
dependencies = [
"cumulus-primitives-core",
"frame-support",
+ "parity-scale-codec",
+ "scale-info",
"smallvec 1.11.0",
+ "sp-arithmetic 16.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0)",
"sp-core 21.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0)",
"sp-runtime 24.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0)",
]
diff --git a/Cargo.toml b/Cargo.toml
index 79728142f..60d215d64 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,7 @@ members = [
"standalone/runtime/evm_tracer",
"pallets/claims",
"pallets/transaction-pause",
+ "pallets/roles",
"precompiles/utils",
"precompiles/utils/macro",
"precompiles/utils/tests-external",
@@ -83,6 +84,7 @@ tangle-runtime = { package = "tangle-standalone-runtime", path = "standalone/run
# Tangle Dependencies
pallet-ecdsa-claims = { path = "pallets/claims", default-features = false }
+pallet-roles = { path = "pallets/roles", default-features = false }
# Orml dependencies
orml-currencies = { git = "https://github.com/open-web3-stack/open-runtime-module-library.git", branch = "polkadot-v1.0.0", default-features = false }
@@ -121,6 +123,8 @@ sc-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch
sc-network-sync = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false }
sp-trie = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false }
+sp-rpc = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
+sp-weights = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false }
frame-election-provider-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false }
diff --git a/pallets/roles/Cargo.toml b/pallets/roles/Cargo.toml
new file mode 100644
index 000000000..a7741be0c
--- /dev/null
+++ b/pallets/roles/Cargo.toml
@@ -0,0 +1,54 @@
+[package]
+name = "pallet-roles"
+version = { workspace = true }
+authors = { workspace = true }
+edition = { workspace = true }
+license = { workspace = true }
+homepage = { workspace = true }
+repository = { workspace = true }
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[dependencies]
+parity-scale-codec = { workspace = true, features = ["derive", "max-encoded-len"] }
+scale-info = { workspace = true }
+serde = { workspace = true }
+frame-support = { workspace = true }
+frame-system = { workspace = true }
+pallet-balances = { workspace = true }
+sp-io = { workspace = true }
+sp-runtime = { workspace = true }
+sp-std = { workspace = true }
+tangle-primitives = {workspace = true, default-features = false }
+frame-benchmarking = { workspace = true, optional = true }
+
+[dev-dependencies]
+hex-literal = { workspace = true }
+sp-core = { workspace = true }
+sp-runtime = { workspace = true }
+serde_json = { workspace = true }
+
+[features]
+default = ["std"]
+std = [
+ "serde/std",
+ "parity-scale-codec/std",
+ "scale-info/std",
+ "frame-support/std",
+ "frame-system/std",
+ "sp-runtime/std",
+ "sp-std/std",
+ "sp-io/std",
+ "pallet-balances/std",
+ "tangle-primitives/std"
+]
+
+try-runtime = ["frame-support/try-runtime"]
+runtime-benchmarks = [
+ "frame-benchmarking/runtime-benchmarks",
+ "frame-support/runtime-benchmarks",
+ "frame-system/runtime-benchmarks",
+ "sp-runtime/runtime-benchmarks",
+ "pallet-balances/runtime-benchmarks"
+]
diff --git a/pallets/roles/src/impls.rs b/pallets/roles/src/impls.rs
new file mode 100644
index 000000000..4637d972d
--- /dev/null
+++ b/pallets/roles/src/impls.rs
@@ -0,0 +1,137 @@
+// This file is part of Tangle.
+// Copyright (C) 2022-2023 Webb Technologies Inc.
+//
+// Tangle 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.
+//
+// Tangle 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 Tangle. If not, see .
+
+use super::*;
+use frame_support::{pallet_prelude::DispatchResult, traits::WithdrawReasons};
+use sp_runtime::Saturating;
+use tangle_primitives::{roles::RoleType, traits::roles::RolesHandler};
+
+/// Implements RolesHandler for the pallet.
+impl RolesHandler for Pallet {
+ /// Validates if the given address has the given role.
+ ///
+ /// # Parameters
+ /// - `address`: The account ID of the validator.
+ /// - `role`: The key representing the type of job.
+ ///
+ /// # Returns
+ /// Returns `true` if the validator is permitted to work with this job type, otherwise `false`.
+ fn validate_role(address: T::AccountId, role: RoleType) -> bool {
+ let assigned_role = AccountRolesMapping::::get(address);
+ match assigned_role {
+ Some(r) =>
+ if r == role {
+ return true
+ },
+ None => return false,
+ }
+
+ false
+ }
+
+ /// Slash validator stake for the reported offence. The function should be a best effort
+ /// slashing, slash upto max possible by the offence type.
+ ///
+ /// # Parameters
+ /// - `address`: The account ID of the validator.
+ /// - `offence`: The offence reported against the validator
+ ///
+ /// # Returns
+ /// DispatchResult emitting `Slashed` event if validator is slashed
+ fn slash_validator(
+ address: T::AccountId,
+ _offence: tangle_primitives::jobs::ValidatorOffence,
+ ) -> sp_runtime::DispatchResult {
+ // TODO: implement calculation of slash amount.
+ let slash_amount = 1000u64;
+ Self::do_slash(address, slash_amount.into())?;
+ Ok(())
+ }
+}
+
+/// Functions for the pallet.
+impl Pallet {
+ /// Get the total amount of the balance that is locked for the given stash.
+ ///
+ /// # Parameters
+ /// - `stash`: The stash account ID.
+ ///
+ /// # Returns
+ /// The total amount of the balance that can be slashed.
+ pub fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf {
+ // Weight note: consider making the stake accessible through stash.
+ Self::ledger(&stash).map(|l| l.total_locked).unwrap_or_default()
+ }
+
+ /// Slash the given amount from the stash account.
+ ///
+ /// # Parameters
+ /// - `address`: The stash account ID.
+ /// - `slash_amount`: The amount to be slashed.
+ pub(crate) fn do_slash(
+ address: T::AccountId,
+ slash_amount: T::CurrencyBalance,
+ ) -> sp_runtime::DispatchResult {
+ let mut ledger = Self::ledger(&address).ok_or(Error::::InvalidStashController)?;
+ let (_imbalance, _missing) = T::Currency::slash(&address, slash_amount.into());
+ ledger.total_locked = ledger.total_locked.saturating_sub(slash_amount.into());
+ Self::update_ledger(&address, &ledger);
+ Self::deposit_event(Event::Slashed { account: address, amount: slash_amount });
+ Ok(())
+ }
+
+ /// Update the ledger for the given stash account.
+ ///
+ /// # Parameters
+ /// - `staker`: The stash account ID.
+ /// - `ledger`: The new ledger.
+ ///
+ /// # Note
+ /// This function will set a lock on the stash account.
+ pub(crate) fn update_ledger(staker: &T::AccountId, ledger: &RoleStakingLedger) {
+ T::Currency::set_lock(
+ ROLES_STAKING_ID,
+ &ledger.stash,
+ ledger.total_locked,
+ WithdrawReasons::all(),
+ );
+ >::insert(staker, ledger);
+ }
+
+ /// Kill the stash account and remove all related information.
+ pub(crate) fn kill_stash(stash: &T::AccountId) -> DispatchResult {
+ >::remove(&stash);
+ Ok(())
+ }
+
+ /// Unbond the stash account.
+ ///
+ /// # Parameters
+ /// - `ledger`: The ledger of the stash account.
+ ///
+ /// # Note
+ /// This function will remove the lock on the stash account.
+ pub(super) fn unbond(ledger: &RoleStakingLedger) -> DispatchResult {
+ let stash = ledger.stash.clone();
+ if ledger.total_locked > T::Currency::minimum_balance() {
+ // Remove the lock.
+ T::Currency::remove_lock(ROLES_STAKING_ID, &stash);
+ // Kill the stash and related information.
+ Self::kill_stash(&stash)?;
+ }
+ Ok(())
+ }
+}
diff --git a/pallets/roles/src/lib.rs b/pallets/roles/src/lib.rs
new file mode 100644
index 000000000..5db090c4b
--- /dev/null
+++ b/pallets/roles/src/lib.rs
@@ -0,0 +1,260 @@
+// This file is part of Tangle.
+// Copyright (C) 2022-2023 Webb Technologies Inc.
+//
+// Tangle 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.
+//
+// Tangle 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 Tangle. If not, see .
+
+//! Pallet to process claims from Ethereum addresses.
+#![cfg_attr(not(feature = "std"), no_std)]
+#![allow(clippy::all)]
+
+use codec::MaxEncodedLen;
+use frame_support::{
+ ensure,
+ traits::{Currency, Get, LockIdentifier, LockableCurrency, OnUnbalanced},
+ CloneNoBound, EqNoBound, PalletId, PartialEqNoBound, RuntimeDebugNoBound,
+};
+pub use pallet::*;
+use parity_scale_codec::{Decode, Encode};
+use scale_info::TypeInfo;
+use sp_runtime::{codec, traits::Zero};
+use sp_std::{convert::TryInto, prelude::*, vec};
+use tangle_primitives::{roles::RoleType, traits::roles::RolesHandler};
+mod impls;
+#[cfg(test)]
+pub(crate) mod mock;
+#[cfg(test)]
+mod tests;
+mod weights;
+pub use weights::WeightInfo;
+
+/// The ledger of a (bonded) stash.
+#[derive(
+ PartialEqNoBound,
+ EqNoBound,
+ CloneNoBound,
+ Encode,
+ Decode,
+ RuntimeDebugNoBound,
+ TypeInfo,
+ MaxEncodedLen,
+)]
+#[scale_info(skip_type_params(T))]
+pub struct RoleStakingLedger {
+ /// The stash account whose balance is actually locked and at stake.
+ pub stash: T::AccountId,
+ /// The total amount of the stash's balance that we are currently accounting for.
+ /// It's just `active` plus all the `unlocking` balances.
+ #[codec(compact)]
+ pub total_locked: BalanceOf,
+}
+
+impl RoleStakingLedger {
+ /// Initializes the default object using the given `validator`.
+ pub fn default_from(stash: T::AccountId) -> Self {
+ Self { stash, total_locked: Zero::zero() }
+ }
+
+ /// Returns `true` if the stash account has no funds at all.
+ pub fn is_empty(&self) -> bool {
+ self.total_locked.is_zero()
+ }
+}
+
+pub type CurrencyOf = ::Currency;
+pub type BalanceOf =
+ <::Currency as Currency<::AccountId>>::Balance;
+type NegativeImbalanceOf = <::Currency as Currency<
+ ::AccountId,
+>>::NegativeImbalance;
+
+const ROLES_STAKING_ID: LockIdentifier = *b"rstaking";
+
+#[frame_support::pallet]
+pub mod pallet {
+ use super::*;
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ #[pallet::pallet]
+ #[pallet::without_storage_info]
+ pub struct Pallet(_);
+
+ /// Configuration trait.
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ /// The overarching event type.
+ type RuntimeEvent: From> + IsType<::RuntimeEvent>;
+
+ type Currency: LockableCurrency<
+ Self::AccountId,
+ Moment = BlockNumberFor,
+ Balance = Self::CurrencyBalance,
+ >;
+ /// Just the `Currency::Balance` type; we have this item to allow us to constrain it to
+ /// `From`.
+ type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
+ + codec::FullCodec
+ + Copy
+ + MaybeSerializeDeserialize
+ + sp_std::fmt::Debug
+ + Default
+ + From
+ + TypeInfo
+ + MaxEncodedLen;
+
+ /// Handler for the unbalanced reduction when slashing a staker.
+ type Slash: OnUnbalanced>;
+
+ type WeightInfo: WeightInfo;
+
+ #[pallet::constant]
+ type PalletId: Get;
+ }
+
+ #[pallet::event]
+ #[pallet::generate_deposit(pub fn deposit_event)]
+ pub enum Event {
+ /// Role assigned to the validator.
+ RoleAssigned { account: T::AccountId, role: RoleType },
+ /// Removed validator from role.
+ RoleRemoved { account: T::AccountId, role: RoleType },
+ /// Funds bonded to become a validator.
+ Bonded { account: T::AccountId, amount: BalanceOf },
+ /// Funds unbonded to stop being a validator.
+ Unbonded { account: T::AccountId, amount: BalanceOf },
+ /// Slashed validator.
+ Slashed { account: T::AccountId, amount: BalanceOf },
+ }
+
+ #[pallet::error]
+ pub enum Error {
+ /// Role has already been assigned to provided validator.
+ RoleAlreadyAssigned,
+ /// No role assigned to provided validator.
+ RoleNotAssigned,
+ /// Insufficient bond to become a validator.
+ InsufficientBond,
+ /// Stash controller account already added to Ledger
+ AlreadyPaired,
+ /// Stash controller account not found in ledger
+ InvalidStashController,
+ }
+
+ /// Map from all "controller" accounts to the info regarding the staking.
+ #[pallet::storage]
+ #[pallet::getter(fn ledger)]
+ pub type Ledger =
+ StorageMap<_, Blake2_128Concat, T::AccountId, RoleStakingLedger>;
+
+ #[pallet::storage]
+ #[pallet::getter(fn account_role)]
+ /// Mapping of resource to bridge index
+ pub type AccountRolesMapping = StorageMap<_, Blake2_256, T::AccountId, RoleType>;
+
+ /// The minimum active bond to become and maintain the role.
+ #[pallet::storage]
+ #[pallet::getter(fn min_active_bond)]
+ pub(super) type MinActiveBond = StorageValue<_, BalanceOf, ValueQuery>;
+
+ /// Assigns a role to the validator.
+ ///
+ /// # Parameters
+ ///
+ /// - `origin`: Origin of the transaction.
+ /// - `bond_value`: Amount of funds to bond.
+ /// - `role`: Role to assign to the validator.
+ ///
+ /// This function will return error if
+ /// - Role is already assigned to the validator.
+ /// - Min active bond is not met.
+ #[pallet::call]
+ impl Pallet {
+ #[pallet::weight({0})]
+ #[pallet::call_index(0)]
+ pub fn assign_role(
+ origin: OriginFor,
+ #[pallet::compact] bond_value: BalanceOf,
+ role: RoleType,
+ ) -> DispatchResult {
+ let stash_account = ensure_signed(origin)?;
+ // Check if role is already assigned.
+ ensure!(
+ !AccountRolesMapping::::contains_key(&stash_account),
+ Error::::RoleAlreadyAssigned
+ );
+
+ // Check if stash account is already paired.
+ ensure!(!>::contains_key(&stash_account), Error::::AlreadyPaired);
+
+ // Check if min active bond is met.
+ let min_active_bond = MinActiveBond::::get();
+ ensure!(bond_value > min_active_bond.into(), Error::::InsufficientBond);
+
+ // Bond with stash account.
+ let stash_balance = T::Currency::free_balance(&stash_account);
+ let value = bond_value.min(stash_balance);
+
+ // Update ledger.
+ let item = RoleStakingLedger { stash: stash_account.clone(), total_locked: value };
+ Self::update_ledger(&stash_account, &item);
+
+ Self::deposit_event(Event::::Bonded {
+ account: stash_account.clone(),
+ amount: value,
+ });
+
+ // Add role mapping for the stash account.
+ AccountRolesMapping::::insert(&stash_account, role);
+ Self::deposit_event(Event::::RoleAssigned { account: stash_account.clone(), role });
+ Ok(())
+ }
+
+ /// Removes the role from the validator.
+ ///
+ /// # Parameters
+ ///
+ /// - `origin`: Origin of the transaction.
+ /// - `role`: Role to remove from the validator.
+ ///
+ /// This function will return error if
+ /// - Role is not assigned to the validator.
+ #[pallet::weight({0})]
+ #[pallet::call_index(1)]
+ pub fn clear_role(origin: OriginFor, role: RoleType) -> DispatchResult {
+ let stash_account = ensure_signed(origin)?;
+ // check if role is assigned.
+ ensure!(
+ Self::validate_role(stash_account.clone(), role.clone()),
+ Error::::RoleNotAssigned
+ );
+ // TODO: Call jobs manager to remove the services.
+ // On successful removal of services, remove the role from the mapping.
+ // Issue link for reference : https://github.com/webb-tools/tangle/issues/292
+
+ // Unbound locked funds.
+ let ledger = Self::ledger(&stash_account).ok_or(Error::::InvalidStashController)?;
+ Self::unbond(&ledger)?;
+ Self::deposit_event(Event::::Unbonded {
+ account: ledger.stash,
+ amount: ledger.total_locked,
+ });
+
+ // Remove role from the mapping.
+ AccountRolesMapping::::remove(&stash_account);
+ Self::deposit_event(Event::::RoleRemoved { account: stash_account, role });
+
+ Ok(())
+ }
+ }
+}
diff --git a/pallets/roles/src/mock.rs b/pallets/roles/src/mock.rs
new file mode 100644
index 000000000..f4cc2c38c
--- /dev/null
+++ b/pallets/roles/src/mock.rs
@@ -0,0 +1,129 @@
+// This file is part of Tangle.
+// Copyright (C) 2022-2023 Webb Technologies Inc.
+//
+// Tangle 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.
+//
+// Tangle 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 Tangle. If not, see .
+#![cfg(test)]
+
+use super::*;
+use crate as pallet_roles;
+use frame_support::{
+ construct_runtime, parameter_types,
+ traits::{ConstU128, ConstU32, ConstU64, Everything},
+};
+
+use sp_core::H256;
+use sp_runtime::{traits::IdentityLookup, BuildStorage};
+pub type AccountId = u128;
+pub type Balance = u128;
+
+impl frame_system::Config for Runtime {
+ type RuntimeOrigin = RuntimeOrigin;
+ type Nonce = u64;
+ type RuntimeCall = RuntimeCall;
+ type Hash = H256;
+ type Hashing = ::sp_runtime::traits::BlakeTwo256;
+ type AccountId = AccountId;
+ type Block = Block;
+ type Lookup = IdentityLookup;
+ type RuntimeEvent = RuntimeEvent;
+ type BlockHashCount = ConstU64<250>;
+ type BlockWeights = ();
+ type BlockLength = ();
+ type Version = ();
+ type PalletInfo = PalletInfo;
+ type AccountData = pallet_balances::AccountData;
+ type OnNewAccount = ();
+ type OnKilledAccount = ();
+ type DbWeight = ();
+ type BaseCallFilter = Everything;
+ type SystemWeightInfo = ();
+ type SS58Prefix = ();
+ type OnSetCode = ();
+ type MaxConsumers = ConstU32<16>;
+}
+
+impl pallet_balances::Config for Runtime {
+ type Balance = Balance;
+ type DustRemoval = ();
+ type RuntimeEvent = RuntimeEvent;
+ type ExistentialDeposit = ConstU128<1>;
+ type AccountStore = System;
+ type MaxLocks = ();
+ type MaxReserves = ConstU32<50>;
+ type ReserveIdentifier = ();
+ type WeightInfo = ();
+ type RuntimeHoldReason = RuntimeHoldReason;
+ type MaxHolds = ();
+ type FreezeIdentifier = ();
+ type MaxFreezes = ();
+}
+
+parameter_types! {
+ pub const RolesPalletId: PalletId = PalletId(*b"py/roles");
+}
+
+impl Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type Currency = Balances;
+ type CurrencyBalance = ::Balance;
+ type Slash = ();
+ type PalletId = RolesPalletId;
+ type WeightInfo = ();
+}
+
+type Block = frame_system::mocking::MockBlock;
+
+construct_runtime!(
+ pub enum Runtime
+ {
+ System: frame_system,
+ Balances: pallet_balances,
+ Roles: pallet_roles,
+ }
+);
+
+pub struct ExtBuilder;
+
+impl Default for ExtBuilder {
+ fn default() -> Self {
+ ExtBuilder
+ }
+}
+
+// Checks events against the latest. A contiguous set of events must be
+// provided. They must include the most recent RuntimeEvent, but do not have to include
+// every past RuntimeEvent.
+pub fn assert_events(mut expected: Vec) {
+ let mut actual: Vec = System::events().iter().map(|e| e.event.clone()).collect();
+
+ expected.reverse();
+ for evt in expected {
+ let next = actual.pop().expect("RuntimeEvent expected");
+ assert_eq!(next, evt, "Events don't match (actual,expected)");
+ }
+}
+
+// This function basically just builds a genesis storage key/value store according to
+// our desired mockup.
+pub fn new_test_ext() -> sp_io::TestExternalities {
+ let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap();
+ // We use default for brevity, but you can configure as desired if needed.
+ pallet_balances::GenesisConfig:: { balances: vec![(10, 10000), (20, 10000)] }
+ .assimilate_storage(&mut t)
+ .unwrap();
+
+ let mut ext = sp_io::TestExternalities::new(t);
+ ext.execute_with(|| System::set_block_number(1));
+ ext
+}
diff --git a/pallets/roles/src/tests.rs b/pallets/roles/src/tests.rs
new file mode 100644
index 000000000..38abccadd
--- /dev/null
+++ b/pallets/roles/src/tests.rs
@@ -0,0 +1,92 @@
+// This file is part of Tangle.
+// Copyright (C) 2022-2023 Webb Technologies Inc.
+//
+// Tangle 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.
+//
+// Tangle 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 Tangle. If not, see .
+#![cfg(test)]
+use super::*;
+use frame_support::assert_ok;
+use mock::*;
+use tangle_primitives::jobs::ValidatorOffence;
+
+#[test]
+fn test_assign_role() {
+ new_test_ext().execute_with(|| {
+ // Initially account if funded with 10000 tokens and we are trying to bond 5000 tokens
+ assert_ok!(Roles::assign_role(RuntimeOrigin::signed(10), 5000, RoleType::Tss));
+
+ assert_events(vec![
+ RuntimeEvent::Roles(crate::Event::Bonded { account: 10, amount: 5000 }),
+ RuntimeEvent::Roles(crate::Event::RoleAssigned { account: 10, role: RoleType::Tss }),
+ ]);
+
+ // Lets verify role assigned to account.
+ assert_eq!(Roles::account_role(10), Some(RoleType::Tss));
+ // Verify ledger mapping
+ assert_eq!(Roles::ledger(10), Some(RoleStakingLedger { stash: 10, total_locked: 5000 }));
+ // Verify total usable balance of the account. Since we have bonded 5000 tokens, we should
+ // have 5000 tokens usable.
+ assert_eq!(Balances::usable_balance(10), 5000);
+ });
+}
+
+#[test]
+fn test_clear_role() {
+ new_test_ext().execute_with(|| {
+ // Initially account if funded with 10000 tokens and we are trying to bond 5000 tokens
+ assert_ok!(Roles::assign_role(RuntimeOrigin::signed(10), 5000, RoleType::Tss));
+ // Verify total usable balance of the account. Since we have bonded 5000 tokens, we should
+ // have 5000 tokens usable.
+ assert_eq!(Balances::usable_balance(10), 5000);
+
+ // Now lets clear the role
+ assert_ok!(Roles::clear_role(RuntimeOrigin::signed(10), RoleType::Tss));
+
+ assert_events(vec![
+ RuntimeEvent::Roles(crate::Event::Unbonded { account: 10, amount: 5000 }),
+ RuntimeEvent::Roles(crate::Event::RoleRemoved { account: 10, role: RoleType::Tss }),
+ ]);
+
+ // Role should be removed from account role mappings.
+ assert_eq!(Roles::account_role(10), None);
+
+ // Ledger should be removed from ledger mappings.
+ assert_eq!(Roles::ledger(10), None);
+
+ // Verify total usable balance of the account. Since we have cleared the role, we should
+ // have 10000 tokens usable.
+ assert_eq!(Balances::usable_balance(10), 10000);
+ });
+}
+
+// test slashing
+#[test]
+fn test_slash_validator() {
+ new_test_ext().execute_with(|| {
+ // Initially account if funded with 10000 tokens and we are trying to bond 5000 tokens
+ assert_ok!(Roles::assign_role(RuntimeOrigin::signed(10), 5000, RoleType::Tss));
+ // Verify total usable balance of the account. Since we have bonded 5000 tokens, we should
+ // have 5000 tokens usable.
+ assert_eq!(Balances::usable_balance(10), 5000);
+
+ // Now lets slash the account for being Inactive.
+ assert_ok!(Roles::slash_validator(10, ValidatorOffence::Inactivity));
+
+ assert_events(vec![RuntimeEvent::Roles(crate::Event::Slashed {
+ account: 10,
+ amount: 1000,
+ })]);
+ // should be updated in ledger
+ assert_eq!(Roles::ledger(10), Some(RoleStakingLedger { stash: 10, total_locked: 4000 }));
+ });
+}
diff --git a/pallets/roles/src/weights.rs b/pallets/roles/src/weights.rs
new file mode 100644
index 000000000..bcb3f0e67
--- /dev/null
+++ b/pallets/roles/src/weights.rs
@@ -0,0 +1,68 @@
+// This file is part of Tangle.
+// Copyright (C) 2022-2023 Webb Technologies Inc.
+//
+// Tangle 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.
+//
+// Tangle 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 Tangle. If not, see .
+
+//! Autogenerated weights for module_transaction_pause
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
+//! DATE: 2021-08-16, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128
+
+// Executed Command:
+// target/release/tangle-standalone
+// benchmark
+// --chain=dev
+// --steps=50
+// --repeat=20
+// --pallet=module_transaction_pause
+// --extrinsic=*
+// --execution=wasm
+// --wasm-execution=compiled
+// --heap-pages=4096
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(clippy::unnecessary_cast)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for roles pallet.
+pub trait WeightInfo {
+ fn assign_role() -> Weight;
+ fn clear_role() -> Weight;
+}
+
+/// Weights for roles pallet.
+pub struct TestWeightInfo(PhantomData);
+impl WeightInfo for TestWeightInfo {
+ fn assign_role() -> Weight {
+ Weight::from_parts(0, 0)
+ }
+ fn clear_role() -> Weight {
+ Weight::from_parts(0, 0)
+ }
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+ fn assign_role() -> Weight {
+ Weight::from_parts(0, 0)
+ }
+ fn clear_role() -> Weight {
+ Weight::from_parts(0, 0)
+ }
+}
\ No newline at end of file
diff --git a/precompiles/staking/Cargo.toml b/precompiles/staking/Cargo.toml
index d4a19e4d1..650955a16 100644
--- a/precompiles/staking/Cargo.toml
+++ b/precompiles/staking/Cargo.toml
@@ -39,6 +39,7 @@ precompile-utils = { workspace = true, features = [ "std", "testing" ] }
# Substrate
pallet-balances = { workspace = true, features = [ "std" ] }
+pallet-dkg-proposals = { workspace = true, features = [ "std" ] }
pallet-timestamp = { workspace = true, features = [ "std" ] }
scale-info = { workspace = true, features = [ "derive", "std" ] }
sp-runtime = { workspace = true, features = [ "std" ] }
diff --git a/precompiles/staking/src/mock.rs b/precompiles/staking/src/mock.rs
index f8e513c09..18277772a 100644
--- a/precompiles/staking/src/mock.rs
+++ b/precompiles/staking/src/mock.rs
@@ -220,6 +220,7 @@ impl pallet_dkg_metadata::Config for Runtime {
type OnDKGPublicKeyChangeHandler = ();
type OffChainAuthId = dkg_runtime_primitives::offchain::crypto::OffchainAuthId;
type NextSessionRotation = pallet_session::PeriodicSessions;
+ type DKGAuthorityToMerkleLeaf = pallet_dkg_proposals::DKGEcdsaToEthereumAddress;
type ForceOrigin = frame_system::EnsureRoot;
type KeygenJailSentence = Period;
type SigningJailSentence = Period;
diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml
index 3e6849f7d..ff749c7db 100644
--- a/primitives/Cargo.toml
+++ b/primitives/Cargo.toml
@@ -12,7 +12,10 @@ frame-support = { workspace = true }
smallvec = { workspace = true }
sp-core = { workspace = true }
sp-runtime = { workspace = true }
+sp-arithmetic = { workspace = true }
cumulus-primitives-core = { workspace = true }
+parity-scale-codec = { workspace = true }
+scale-info = {workspace = true}
[features]
default = ["std"]
diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs
index 54c739388..d80756acf 100644
--- a/primitives/src/lib.rs
+++ b/primitives/src/lib.rs
@@ -29,6 +29,7 @@ use sp_runtime::{
pub mod types;
pub use types::*;
+pub mod traits;
/// Tangle parachain time-related
pub mod time {
diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs
new file mode 100644
index 000000000..ad3e620aa
--- /dev/null
+++ b/primitives/src/traits.rs
@@ -0,0 +1 @@
+pub mod roles;
diff --git a/primitives/src/traits/roles.rs b/primitives/src/traits/roles.rs
new file mode 100644
index 000000000..13db8613b
--- /dev/null
+++ b/primitives/src/traits/roles.rs
@@ -0,0 +1,46 @@
+// This file is part of Webb.
+// Copyright (C) 2022 Webb Technologies Inc.
+//
+// Tangle 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.
+//
+// Tangle 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 Tangle. If not, see .
+
+use crate::{jobs::ValidatorOffence, roles::RoleType};
+use sp_runtime::DispatchResult;
+
+/// A trait that handles roles associated with job types.
+pub trait RolesHandler {
+ /// Returns true if the validator is permitted to work with this job type.
+ ///
+ /// # Parameters
+ ///
+ /// - `address`: The account ID of the validator.
+ /// - `role_type`: The key representing the type of job.
+ ///
+ /// # Returns
+ ///
+ /// Returns `true` if the validator is permitted to work with this job type, otherwise `false`.
+ fn validate_role(address: AccountId, role_type: RoleType) -> bool;
+
+ /// Slash validator stake for the reported offence. The function should be a best effort
+ /// slashing, slash upto max possible by the offence type.
+ ///
+ /// # Parameters
+ ///
+ /// - `address`: The account ID of the validator.
+ /// - `offence`: The offence reported against the validator
+ ///
+ /// # Returns
+ ///
+ /// Returns Ok() if the address is a validator and was slashed
+ fn slash_validator(address: AccountId, offence: ValidatorOffence) -> DispatchResult;
+}
diff --git a/primitives/src/types.rs b/primitives/src/types.rs
index 8569e906a..996209737 100644
--- a/primitives/src/types.rs
+++ b/primitives/src/types.rs
@@ -15,7 +15,8 @@ use sp_runtime::AccountId32;
// limitations under the License.
//
use super::*;
-
+pub mod jobs;
+pub mod roles;
/// Reputation type
pub type Reputation = u128;
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
diff --git a/primitives/src/types/jobs.rs b/primitives/src/types/jobs.rs
new file mode 100644
index 000000000..d293ea9e9
--- /dev/null
+++ b/primitives/src/types/jobs.rs
@@ -0,0 +1,25 @@
+// This file is part of Webb.
+// Copyright (C) 2022 Webb Technologies Inc.
+//
+// Tangle 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.
+//
+// Tangle 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 Tangle. If not, see .
+
+use frame_support::{pallet_prelude::*, RuntimeDebug};
+/// Represents different types of validator offences.
+#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Clone)]
+pub enum ValidatorOffence {
+ /// The validator has been inactive.
+ Inactivity,
+ /// The validator has committed duplicate signing.
+ Equivocation,
+}
diff --git a/primitives/src/types/roles.rs b/primitives/src/types/roles.rs
new file mode 100644
index 000000000..9f687ac83
--- /dev/null
+++ b/primitives/src/types/roles.rs
@@ -0,0 +1,36 @@
+// This file is part of Webb.
+// Copyright (C) 2022 Webb Technologies Inc.
+//
+// Tangle 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.
+//
+// Tangle 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 Tangle. If not, see .
+
+use frame_support::pallet_prelude::*;
+
+/// Role type to be used in the system.
+#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)]
+pub enum RoleType {
+ Tss,
+ ZkSaas,
+}
+
+impl RoleType {
+ /// Checks if the role type is a TSS role.
+ pub fn is_tss(self) -> bool {
+ self == RoleType::Tss
+ }
+
+ /// Checks if the role type is a Zk-Saas role.
+ pub fn is_zksaas(self) -> bool {
+ self == RoleType::ZkSaas
+ }
+}