Skip to content

Commit

Permalink
feat: Add Support for Master Blueprint Service Manager
Browse files Browse the repository at this point in the history
  • Loading branch information
shekohex committed Nov 21, 2024
1 parent b535f8c commit c11fa33
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 40 deletions.
106 changes: 95 additions & 11 deletions pallets/services/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use frame_support::dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo};
use sp_core::{H160, U256};
use sp_runtime::traits::UniqueSaturatedInto;
use tangle_primitives::services::{
BlueprintManager, Field, OperatorPreferences, Service, ServiceBlueprint,
BlueprintServiceManager, Field, MasterBlueprintServiceManagerRevision, OperatorPreferences,
Service, ServiceBlueprint,
};

use super::*;
Expand All @@ -38,6 +39,89 @@ impl<T: Config> Pallet<T> {
T::PalletEVMAddress::get()
}

/// Get the address of the master blueprint service manager at a given revision.
///
/// # Parameters
/// * `revision` - The revision of the master blueprint service manager.
///
/// # Returns
/// * `Result<H160, Error<T>>` - The address of the master blueprint service manager.
/// * `Error<T>` - The error type.
#[doc(alias = "get_master_blueprint_service_manager_address")]
pub fn mbsm_address(revision: u32) -> Result<H160, Error<T>> {
MasterBlueprintServiceManagerRevisions::<T>::get()
.get(revision as usize)
.cloned()
.ok_or(Error::<T>::MasterBlueprintServiceManagerRevisionNotFound)
}

/// Get the latest revision of the master blueprint service manager.
///
/// # Returns
/// * `u32` - The latest revision of the master blueprint service manager.
#[doc(alias = "get_master_blueprint_service_manager_revision")]
pub fn mbsm_latest_revision() -> u32 {
MasterBlueprintServiceManagerRevisions::<T>::decode_len()
.map(|len| len.saturating_sub(1) as u32)
.unwrap_or(0)
}

/// Get the address of the master blueprint service manager for the given blueprint.
///
/// # Parameters
/// * `blueprint` - The service blueprint.
///
/// # Returns
/// * `Result<H160, Error<T>>` - The address of the master blueprint service manager.
/// * `Error<T>` - The error type.
pub fn mbsm_address_of(blueprint: &ServiceBlueprint<T::Constraints>) -> Result<H160, Error<T>> {
match blueprint.master_manager_revision {
MasterBlueprintServiceManagerRevision::Latest => {
Self::mbsm_address(Self::mbsm_latest_revision())
},
MasterBlueprintServiceManagerRevision::Specific(rev) => Self::mbsm_address(rev),
other => unimplemented!("Got unexpected case for {:?}", other),
}
}

pub fn on_blueprint_created_hook(
blueprint: &ServiceBlueprint<T::Constraints>,
blueprint_id: u64,
owner: &T::AccountId,
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
let mbsm = Self::mbsm_address_of(blueprint)?;
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onBlueprintCreated"),
inputs: vec![
ethabi::Param {
name: String::from("blueprintId"),
kind: ethabi::ParamType::Uint(64),
internal_type: None,
},
ethabi::Param {
name: String::from("owner"),
kind: ethabi::ParamType::Address,
internal_type: None,
},
ServiceBlueprint::<T::Constraints>::to_ethabi_param(),
],
outputs: Default::default(),
constant: None,
state_mutability: ethabi::StateMutability::Payable,
};

let blueprint_id = Token::Uint(ethabi::Uint::from(blueprint_id));
let owner = Token::Address(T::EvmAddressMapping::into_address(owner.clone()));
let blueprint = blueprint.to_ethabi();
let data = call
.encode_input(&[blueprint_id, owner, blueprint])
.map_err(|_| Error::<T>::EVMAbiEncode)?;
let gas_limit = 300_000;
let info = Self::evm_call(Self::address(), mbsm, 0.into(), data, gas_limit)?;
Ok((info.exit_reason.is_succeed(), Self::weight_from_call_info(&info)))
}

/// Hook to be called upon a new operator registration on a blueprint.
///
/// This function is called when a service is registered. It performs an EVM call
Expand All @@ -59,7 +143,7 @@ impl<T: Config> Pallet<T> {
value: BalanceOf<T>,
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
let (allowed, weight) = match blueprint.manager {
BlueprintManager::Evm(contract) => {
BlueprintServiceManager::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onRegister"),
Expand Down Expand Up @@ -111,7 +195,7 @@ impl<T: Config> Pallet<T> {
prefrences: &OperatorPreferences,
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
match blueprint.manager {
BlueprintManager::Evm(contract) => {
BlueprintServiceManager::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onUnregister"),
Expand Down Expand Up @@ -149,7 +233,7 @@ impl<T: Config> Pallet<T> {
prefrences: &OperatorPreferences,
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
match blueprint.manager {
BlueprintManager::Evm(contract) => {
BlueprintServiceManager::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onUpdatePriceTargets"),
Expand Down Expand Up @@ -191,7 +275,7 @@ impl<T: Config> Pallet<T> {
restaking_percent: u8,
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
match blueprint.manager {
BlueprintManager::Evm(contract) => {
BlueprintServiceManager::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onApprove"),
Expand Down Expand Up @@ -248,7 +332,7 @@ impl<T: Config> Pallet<T> {
request_id: u64,
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
match blueprint.manager {
BlueprintManager::Evm(contract) => {
BlueprintServiceManager::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onReject"),
Expand Down Expand Up @@ -312,7 +396,7 @@ impl<T: Config> Pallet<T> {
value: BalanceOf<T>,
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
let (allowed, weight) = match blueprint.manager {
BlueprintManager::Evm(contract) => {
BlueprintServiceManager::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onRequest"),
Expand Down Expand Up @@ -425,7 +509,7 @@ impl<T: Config> Pallet<T> {
ttl: BlockNumberFor<T>,
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
let (allowed, weight) = match blueprint.manager {
BlueprintManager::Evm(contract) => {
BlueprintServiceManager::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onServiceInitialized"),
Expand Down Expand Up @@ -514,7 +598,7 @@ impl<T: Config> Pallet<T> {
owner: &T::AccountId,
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
let (allowed, weight) = match blueprint.manager {
BlueprintManager::Evm(contract) => {
BlueprintServiceManager::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onServiceTermination"),
Expand Down Expand Up @@ -572,7 +656,7 @@ impl<T: Config> Pallet<T> {
inputs: &[Field<T::Constraints, T::AccountId>],
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
let (allowed, weight) = match blueprint.manager {
BlueprintManager::Evm(contract) => {
BlueprintServiceManager::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onJobCall"),
Expand Down Expand Up @@ -647,7 +731,7 @@ impl<T: Config> Pallet<T> {
outputs: &[Field<T::Constraints, T::AccountId>],
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
let (allowed, weight) = match blueprint.manager {
BlueprintManager::Evm(contract) => {
BlueprintServiceManager::Evm(contract) => {
#[allow(deprecated)]
let call = ethabi::Function {
name: String::from("onJobResult"),
Expand Down
48 changes: 45 additions & 3 deletions pallets/services/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub mod module {
use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize};
use sp_runtime::Percent;
use sp_std::vec::Vec;
use tangle_primitives::services::MasterBlueprintServiceManagerRevision;
use tangle_primitives::{
services::{PriceTargets, *},
MultiAssetDelegationInfo,
Expand Down Expand Up @@ -161,6 +162,12 @@ pub mod module {
/// Maximum number of assets per service.
#[pallet::constant]
type MaxAssetsPerService: Get<u32> + Default + Parameter + MaybeSerializeDeserialize;
/// Maximum number of versions of Master Blueprint Service Manager allowed.
#[pallet::constant]
type MaxMasterBlueprintServiceManagerVersions: Get<u32>
+ Default
+ Parameter
+ MaybeSerializeDeserialize;

/// The constraints for the service module.
/// use [crate::types::ConstraintsOf] with `Self` to implement this trait.
Expand Down Expand Up @@ -202,6 +209,8 @@ pub mod module {
pub enum Error<T> {
/// The service blueprint was not found.
BlueprintNotFound,
/// Blueprint creation is interrupted.
BlueprintCreationInterrupted,
/// The caller is already registered as a operator.
AlreadyRegistered,
/// The caller does not have the requirements to be a operator.
Expand Down Expand Up @@ -274,6 +283,8 @@ pub mod module {
NoDisputeOrigin,
/// The Unapplied Slash are not found.
UnappliedSlashNotFound,
/// The Supplied Master Blueprint Service Manager Revision is not found.
MasterBlueprintServiceManagerRevisionNotFound,
}

#[pallet::event]
Expand Down Expand Up @@ -589,6 +600,14 @@ pub mod module {
ResultQuery<Error<T>::UnappliedSlashNotFound>,
>;

/// All the Master Blueprint Service Managers revisions.
///
/// Where the index is the revision number.
#[pallet::storage]
#[pallet::getter(fn mbsm_revisions)]
pub type MasterBlueprintServiceManagerRevisions<T: Config> =
StorageValue<_, BoundedVec<H160, T::MaxMasterBlueprintServiceManagerVersions>, ValueQuery>;

// *** auxiliary storage and maps ***
#[pallet::storage]
#[pallet::getter(fn operator_profile)]
Expand All @@ -613,15 +632,38 @@ pub mod module {
#[pallet::weight(T::WeightInfo::create_blueprint())]
pub fn create_blueprint(
origin: OriginFor<T>,
blueprint: ServiceBlueprint<T::Constraints>,
) -> DispatchResult {
mut blueprint: ServiceBlueprint<T::Constraints>,
) -> DispatchResultWithPostInfo {
let owner = ensure_signed(origin)?;
let blueprint_id = Self::next_blueprint_id();
// Ensure the master blueprint service manager exists and if it uses
// latest, pin it to the latest revision.
match blueprint.master_manager_revision {
MasterBlueprintServiceManagerRevision::Latest => {
let latest_revision = Self::mbsm_latest_revision();
blueprint.master_manager_revision =
MasterBlueprintServiceManagerRevision::Specific(latest_revision);
},
MasterBlueprintServiceManagerRevision::Specific(rev) => {
ensure!(
rev <= Self::mbsm_latest_revision(),
Error::<T>::MasterBlueprintServiceManagerRevisionNotFound,
);
},
_ => unreachable!("MasterBlueprintServiceManagerRevision case is not implemented"),
};

let (allowed, _weight) =
Self::on_blueprint_created_hook(&blueprint, blueprint_id, &owner)?;

ensure!(allowed, Error::<T>::BlueprintCreationInterrupted);

Blueprints::<T>::insert(blueprint_id, (owner.clone(), blueprint));
NextBlueprintId::<T>::set(blueprint_id.saturating_add(1));

Self::deposit_event(Event::BlueprintCreated { owner, blueprint_id });
Ok(())
// TODO: update weight for the creation of the blueprint.
Ok(PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes })
}

/// Pre-register the caller as an operator for a specific blueprint.
Expand Down
5 changes: 5 additions & 0 deletions pallets/services/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@ parameter_types! {
#[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub const SlashDeferDuration: u32 = 7;

#[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub const MaxMasterBlueprintServiceManagerRevisions: u32 = u32::MAX;
}

impl Config for Runtime {
Expand Down Expand Up @@ -397,6 +401,7 @@ impl Config for Runtime {
type MaxContainerImageNameLength = MaxContainerImageNameLength;
type MaxContainerImageTagLength = MaxContainerImageTagLength;
type MaxAssetsPerService = MaxAssetsPerService;
type MaxMasterBlueprintServiceManagerVersions = MaxMasterBlueprintServiceManagerRevisions;
type Constraints = pallet_services::types::ConstraintsOf<Self>;
type OperatorDelegationManager = MockDelegationManager;
type SlashDeferDuration = SlashDeferDuration;
Expand Down
8 changes: 2 additions & 6 deletions pallets/services/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,21 @@ fn cggmp21_blueprint() -> ServiceBlueprint<ConstraintsOf<Runtime>> {
#[allow(deprecated)]
ServiceBlueprint {
metadata: ServiceMetadata { name: "CGGMP21 TSS".try_into().unwrap(), ..Default::default() },
manager: BlueprintManager::Evm(CGGMP21_BLUEPRINT),
manager: BlueprintServiceManager::Evm(CGGMP21_BLUEPRINT),
master_manager_revision: MasterBlueprintServiceManagerRevision::Latest,
jobs: bounded_vec![
JobDefinition {
metadata: JobMetadata { name: "keygen".try_into().unwrap(), ..Default::default() },
params: bounded_vec![FieldType::Uint8],
result: bounded_vec![FieldType::Bytes],
verifier: JobResultVerifier::Evm(CGGMP21_BLUEPRINT),
},
JobDefinition {
metadata: JobMetadata { name: "sign".try_into().unwrap(), ..Default::default() },
params: bounded_vec![FieldType::Uint64, FieldType::Bytes],
result: bounded_vec![FieldType::Bytes],
#[allow(deprecated)]
verifier: JobResultVerifier::Evm(CGGMP21_BLUEPRINT),
},
],
registration_hook: ServiceRegistrationHook::Evm(CGGMP21_BLUEPRINT),
registration_params: bounded_vec![],
request_hook: ServiceRequestHook::Evm(CGGMP21_BLUEPRINT),
request_params: bounded_vec![],
gadget: Default::default(),
}
Expand Down
5 changes: 5 additions & 0 deletions precompiles/services/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,10 @@ parameter_types! {
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub const MaxAssetsPerService: u32 = 164;

#[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub const MaxMasterBlueprintServiceManagerRevisions: u32 = u32::MAX;

#[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub const SlashDeferDuration: u32 = 7;
Expand Down Expand Up @@ -507,6 +511,7 @@ impl pallet_services::Config for Runtime {
type MaxContainerImageNameLength = MaxContainerImageNameLength;
type MaxContainerImageTagLength = MaxContainerImageTagLength;
type MaxAssetsPerService = MaxAssetsPerService;
type MaxMasterBlueprintServiceManagerVersions = MaxMasterBlueprintServiceManagerRevisions;
type Constraints = pallet_services::types::ConstraintsOf<Self>;
type OperatorDelegationManager = MockDelegationManager;
type SlashDeferDuration = SlashDeferDuration;
Expand Down
10 changes: 4 additions & 6 deletions precompiles/services/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ use sp_core::ecdsa;
use sp_core::{H160, U256};
use sp_runtime::bounded_vec;
use sp_runtime::AccountId32;
use tangle_primitives::services::BlueprintManager;
use tangle_primitives::services::BlueprintServiceManager;
use tangle_primitives::services::FieldType;
use tangle_primitives::services::JobDefinition;
use tangle_primitives::services::JobMetadata;
use tangle_primitives::services::JobResultVerifier;
use tangle_primitives::services::MasterBlueprintServiceManagerRevision;
use tangle_primitives::services::PriceTargets;
use tangle_primitives::services::ServiceMetadata;
use tangle_primitives::services::ServiceRegistrationHook;
Expand Down Expand Up @@ -64,25 +65,22 @@ fn cggmp21_blueprint() -> ServiceBlueprint<ConstraintsOf<Runtime>> {
#[allow(deprecated)]
ServiceBlueprint {
metadata: ServiceMetadata { name: "CGGMP21 TSS".try_into().unwrap(), ..Default::default() },
manager: BlueprintManager::Evm(CGGMP21_BLUEPRINT),
manager: BlueprintServiceManager::Evm(CGGMP21_BLUEPRINT),
master_manager_revision: MasterBlueprintServiceManagerRevision::Latest,
jobs: bounded_vec![
JobDefinition {
metadata: JobMetadata { name: "keygen".try_into().unwrap(), ..Default::default() },
params: bounded_vec![FieldType::Uint8],
result: bounded_vec![FieldType::Bytes],
verifier: JobResultVerifier::Evm(CGGMP21_BLUEPRINT),
},
JobDefinition {
metadata: JobMetadata { name: "sign".try_into().unwrap(), ..Default::default() },
params: bounded_vec![FieldType::Uint64, FieldType::Bytes],
result: bounded_vec![FieldType::Bytes],
#[allow(deprecated)]
verifier: JobResultVerifier::Evm(CGGMP21_BLUEPRINT),
},
],
registration_hook: ServiceRegistrationHook::Evm(CGGMP21_BLUEPRINT),
registration_params: bounded_vec![],
request_hook: ServiceRequestHook::Evm(CGGMP21_BLUEPRINT),
request_params: bounded_vec![],
gadget: Default::default(),
}
Expand Down
Loading

0 comments on commit c11fa33

Please sign in to comment.