Skip to content

Commit

Permalink
feat: add roles metadata for validators
Browse files Browse the repository at this point in the history
  • Loading branch information
1xstj committed Nov 22, 2023
1 parent bf7e79a commit aa43b96
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 56 deletions.
16 changes: 4 additions & 12 deletions pallets/jobs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use sp_std::{prelude::*, vec::Vec};
use tangle_primitives::{
jobs::{JobId, JobInfo, JobKey, PhaseOneResult, ValidatorOffence},
traits::{
jobs::{JobResultVerifier, JobToFee},
jobs::{JobToFee, MPCHandler},
roles::RolesHandler,
},
};
Expand Down Expand Up @@ -71,11 +71,7 @@ pub mod module {
type RolesHandler: RolesHandler<Self::AccountId>;

/// The job result verifying mechanism
type JobResultVerifier: JobResultVerifier<
Self::AccountId,
BlockNumberFor<Self>,
BalanceOf<Self>,
>;
type MPCHandler: MPCHandler<Self::AccountId, BlockNumberFor<Self>, BalanceOf<Self>>;

/// The origin which may set filter.
type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
Expand Down Expand Up @@ -339,7 +335,7 @@ pub mod module {
};

// Validate the result
T::JobResultVerifier::verify(&job_info, phase1_result.clone(), result.clone())?;
T::MPCHandler::verify(&job_info, phase1_result.clone(), result.clone())?;

// If phase 1, store in known result
if job_info.job_type.is_phase_one() {
Expand Down Expand Up @@ -483,11 +479,7 @@ pub mod module {
ensure!(participants.contains(&validator), Error::<T>::JobNotFound);

// Validate the result
T::JobResultVerifier::verify_validator_report(
validator.clone(),
offence.clone(),
signatures,
)?;
T::MPCHandler::verify_validator_report(validator.clone(), offence.clone(), signatures)?;

// Slash the validator
T::RolesHandler::slash_validator(validator.clone(), offence)?;
Expand Down
10 changes: 7 additions & 3 deletions pallets/jobs/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ impl RolesHandler<AccountId> for MockRolesHandler {
}
}

pub struct MockJobResultVerifier;
pub struct MockMPCHandler;

impl JobResultVerifier<AccountId, BlockNumber, Balance> for MockJobResultVerifier {
impl MPCHandler<AccountId, BlockNumber, Balance> for MockMPCHandler {
fn verify(
job: &JobInfo<AccountId, BlockNumber, Balance>,
phase_one_data: Option<PhaseOneResult<AccountId, BlockNumber>>,
Expand All @@ -161,6 +161,10 @@ impl JobResultVerifier<AccountId, BlockNumber, Balance> for MockJobResultVerifie
) -> DispatchResult {
Ok(())
}

fn validate_authority_key(_validator: AccountId, _authority_key: Vec<u8>) -> DispatchResult {
Ok(())
}
}

parameter_types! {
Expand All @@ -173,7 +177,7 @@ impl Config for Runtime {
type Currency = Balances;
type JobToFee = MockJobToFeeHandler;
type RolesHandler = MockRolesHandler;
type JobResultVerifier = MockJobResultVerifier;
type MPCHandler = MockMPCHandler;
type PalletId = JobsPalletId;
type WeightInfo = ();
}
Expand Down
22 changes: 11 additions & 11 deletions pallets/roles/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,29 @@
use super::*;
use frame_support::{pallet_prelude::DispatchResult, traits::WithdrawReasons};
use sp_runtime::Saturating;
use tangle_primitives::{roles::RoleType, traits::roles::RolesHandler};
use tangle_primitives::{jobs::JobKey, traits::roles::RolesHandler};

/// Implements RolesHandler for the pallet.
impl<T: Config> RolesHandler<T::AccountId> for Pallet<T> {
/// 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.
/// - `job`: The key representing the type of job.
///
/// # Returns
/// Returns `true` if the validator is permitted to work with this job type, otherwise `false`.
fn is_validator(address: T::AccountId, role: RoleType) -> bool {
fn is_validator(address: T::AccountId, job_key: JobKey) -> bool {
let assigned_role = AccountRolesMapping::<T>::get(address);
match assigned_role {
Some(r) =>
if r == role {
return true
},
None => return false,
}

false
if let Some(assigned_role) = assigned_role {
match job_key {
JobKey::DKG | JobKey::DKGSignature => assigned_role.is_tss(),
JobKey::ZkSaasPhaseOne | JobKey::ZkSaasPhaseTwo => assigned_role.is_zksaas(),
}
} else {
return false
}
}

/// Slash validator stake for the reported offence. The function should be a best effort
Expand Down
16 changes: 13 additions & 3 deletions pallets/roles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ 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};
use tangle_primitives::roles::RoleType;
mod impls;
#[cfg(test)]
pub(crate) mod mock;
Expand Down Expand Up @@ -85,6 +85,7 @@ pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use tangle_primitives::traits::jobs::MPCHandler;

#[pallet::pallet]
#[pallet::without_storage_info]
Expand Down Expand Up @@ -116,6 +117,9 @@ pub mod pallet {
/// Handler for the unbalanced reduction when slashing a staker.
type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;

/// The config that verifies MPC related functions
type MPCHandler: MPCHandler<Self::AccountId, BlockNumberFor<Self>, BalanceOf<Self>>;

type WeightInfo: WeightInfo;

#[pallet::constant]
Expand Down Expand Up @@ -201,6 +205,12 @@ pub mod pallet {
let min_active_bond = MinActiveBond::<T>::get();
ensure!(bond_value > min_active_bond.into(), Error::<T>::InsufficientBond);

// validate the metadata
T::MPCHandler::validate_authority_key(
stash_account.clone(),
role.clone().get_authority_key(),
)?;

// Bond with stash account.
let stash_balance = T::Currency::free_balance(&stash_account);
let value = bond_value.min(stash_balance);
Expand All @@ -215,7 +225,7 @@ pub mod pallet {
});

// Add role mapping for the stash account.
AccountRolesMapping::<T>::insert(&stash_account, role);
AccountRolesMapping::<T>::insert(&stash_account, role.clone());
Self::deposit_event(Event::<T>::RoleAssigned { account: stash_account.clone(), role });
Ok(())
}
Expand All @@ -235,7 +245,7 @@ pub mod pallet {
let stash_account = ensure_signed(origin)?;
// check if role is assigned.
ensure!(
Self::is_validator(stash_account.clone(), role.clone()),
AccountRolesMapping::<T>::get(stash_account.clone()).is_some(),
Error::<T>::RoleNotAssigned
);
// TODO: Call jobs manager to remove the services.
Expand Down
29 changes: 29 additions & 0 deletions pallets/roles/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ use frame_support::{
construct_runtime, parameter_types,
traits::{ConstU128, ConstU32, ConstU64, Everything},
};
use sp_runtime::DispatchResult;
use tangle_primitives::jobs::*;

use sp_core::H256;
use sp_runtime::{traits::IdentityLookup, BuildStorage};
use tangle_primitives::traits::jobs::MPCHandler;
pub type AccountId = u128;
pub type Balance = u128;
pub type BlockNumber = u64;

impl frame_system::Config for Runtime {
type RuntimeOrigin = RuntimeOrigin;
Expand Down Expand Up @@ -69,6 +73,30 @@ impl pallet_balances::Config for Runtime {
type MaxFreezes = ();
}

pub struct MockMPCHandler;

impl MPCHandler<AccountId, BlockNumber, Balance> for MockMPCHandler {
fn verify(
_job: &JobInfo<AccountId, BlockNumber, Balance>,
_phase_one_data: Option<PhaseOneResult<AccountId, BlockNumber>>,
_result: Vec<u8>,
) -> DispatchResult {
Ok(())
}

fn verify_validator_report(
_validator: AccountId,
_offence: ValidatorOffence,
_report: Vec<u8>,
) -> DispatchResult {
Ok(())
}

fn validate_authority_key(_validator: AccountId, _authority_key: Vec<u8>) -> DispatchResult {
Ok(())
}
}

parameter_types! {
pub const RolesPalletId: PalletId = PalletId(*b"py/roles");
}
Expand All @@ -77,6 +105,7 @@ impl Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type CurrencyBalance = <Self as pallet_balances::Config>::Balance;
type MPCHandler = MockMPCHandler;
type Slash = ();
type PalletId = RolesPalletId;
type WeightInfo = ();
Expand Down
34 changes: 26 additions & 8 deletions pallets/roles/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,28 @@
use super::*;
use frame_support::assert_ok;
use mock::*;
use tangle_primitives::jobs::ValidatorOffence;
use tangle_primitives::{jobs::ValidatorOffence, traits::roles::RolesHandler};

#[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_ok!(Roles::assign_role(
RuntimeOrigin::signed(10),
5000,
RoleType::Tss(Default::default())
));

assert_events(vec![
RuntimeEvent::Roles(crate::Event::Bonded { account: 10, amount: 5000 }),
RuntimeEvent::Roles(crate::Event::RoleAssigned { account: 10, role: RoleType::Tss }),
RuntimeEvent::Roles(crate::Event::RoleAssigned {
account: 10,
role: RoleType::Tss(Default::default()),
}),
]);

// Lets verify role assigned to account.
assert_eq!(Roles::account_role(10), Some(RoleType::Tss));
assert_eq!(Roles::account_role(10), Some(RoleType::Tss(Default::default())));
// 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
Expand All @@ -44,17 +51,24 @@ fn test_assign_role() {
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));
assert_ok!(Roles::assign_role(
RuntimeOrigin::signed(10),
5000,
RoleType::Tss(Default::default())
));
// 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_ok!(Roles::clear_role(RuntimeOrigin::signed(10), RoleType::Tss(Default::default())));

assert_events(vec![
RuntimeEvent::Roles(crate::Event::Unbonded { account: 10, amount: 5000 }),
RuntimeEvent::Roles(crate::Event::RoleRemoved { account: 10, role: RoleType::Tss }),
RuntimeEvent::Roles(crate::Event::RoleRemoved {
account: 10,
role: RoleType::Tss(Default::default()),
}),
]);

// Role should be removed from account role mappings.
Expand All @@ -74,7 +88,11 @@ fn test_clear_role() {
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));
assert_ok!(Roles::assign_role(
RuntimeOrigin::signed(10),
5000,
RoleType::Tss(Default::default())
));
// 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);
Expand Down
23 changes: 22 additions & 1 deletion primitives/src/traits/jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub trait JobToFee<AccountId, BlockNumber> {
}

/// A trait that describes the job result verification.
pub trait JobResultVerifier<AccountId, BlockNumber, Balance> {
pub trait MPCHandler<AccountId, BlockNumber, Balance> {
/// Verifies the result of a job.
///
/// # Parameters
Expand All @@ -57,11 +57,32 @@ pub trait JobResultVerifier<AccountId, BlockNumber, Balance> {
result: Vec<u8>,
) -> DispatchResult;

// Verify a validator report
///
/// This function is responsible for verifying a report against a specific validator's
/// offence and taking appropriate actions based on the report.
///
/// # Arguments
///
/// - `validator`: The account ID of the validator being reported.
/// - `offence`: Details of the offence reported against the validator.
/// - `report`: The report data provided by the reporting entity.
fn verify_validator_report(
validator: AccountId,
offence: ValidatorOffence,
report: Vec<u8>,
) -> DispatchResult;

/// Validate the authority key associated with a specific validator.
///
/// This function is responsible for validating the authority key associated with a given
/// validator.
///
/// # Arguments
///
/// - `validator`: The account ID of the validator whose authority key is to be validated.
/// - `authority_key`: The authority key to be validated.
fn validate_authority_key(validator: AccountId, authority_key: Vec<u8>) -> DispatchResult;
}

/// A trait that handles various aspects of jobs for a validator.
Expand Down
6 changes: 3 additions & 3 deletions primitives/src/traits/roles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Tangle. If not, see <http://www.gnu.org/licenses/>.

use crate::{jobs::ValidatorOffence, roles::RoleType};
use crate::jobs::{JobKey, ValidatorOffence};
use sp_runtime::DispatchResult;

/// A trait that handles roles associated with job types.
Expand All @@ -24,12 +24,12 @@ pub trait RolesHandler<AccountId> {
/// # Parameters
///
/// - `address`: The account ID of the validator.
/// - `role_type`: The type of role
/// - `job_key`: The type of job
///
/// # Returns
///
/// Returns `true` if the validator is permitted to work with this job type, otherwise `false`.
fn is_validator(address: AccountId, role_type: RoleType) -> bool;
fn is_validator(address: AccountId, job_key: JobKey) -> bool;

/// Slash validator stake for the reported offence. The function should be a best effort
/// slashing, slash upto max possible by the offence type.
Expand Down
Loading

0 comments on commit aa43b96

Please sign in to comment.