Skip to content

Commit

Permalink
feat!: Select Assets when requesting a service (#785)
Browse files Browse the repository at this point in the history
  • Loading branch information
shekohex authored Oct 9, 2024
1 parent 85ea18b commit 86e098d
Show file tree
Hide file tree
Showing 18 changed files with 182 additions and 48 deletions.
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
packages = [
pkgs.taplo
pkgs.cargo-nextest
pkgs.cargo-tarpaulin
];
# Environment variables
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
Expand Down
5 changes: 3 additions & 2 deletions pallets/services/rpc/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ pub type BlockNumberOf<Block> =
<<Block as sp_runtime::traits::HeaderProvider>::HeaderT as sp_runtime::traits::Header>::Number;

sp_api::decl_runtime_apis! {
pub trait ServicesApi<C, AccountId>
pub trait ServicesApi<C, AccountId, AssetId>
where
C: Constraints,
AccountId: Codec + MaybeDisplay + Serialize,
AssetId: Codec + MaybeDisplay + Serialize,
{
/// Query all the services that this operator is providing along with their blueprints.
///
Expand All @@ -41,7 +42,7 @@ sp_api::decl_runtime_apis! {
fn query_services_with_blueprints_by_operator(
operator: AccountId,
) -> Result<
Vec<RpcServicesWithBlueprint<C, AccountId, BlockNumberOf<Block>>>,
Vec<RpcServicesWithBlueprint<C, AccountId, BlockNumberOf<Block>, AssetId>>,
sp_runtime::DispatchError,
>;
}
Expand Down
14 changes: 8 additions & 6 deletions pallets/services/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,19 @@ type BlockNumberOf<Block> =

/// ServicesClient RPC methods.
#[rpc(client, server)]
pub trait ServicesApi<BlockHash, X, AccountId, BlockNumber>
pub trait ServicesApi<BlockHash, X, AccountId, BlockNumber, AssetId>
where
X: Constraints,
AccountId: Codec + MaybeDisplay + core::fmt::Debug + Send + Sync + 'static + Serialize,
BlockNumber: Codec + MaybeDisplay + core::fmt::Debug + Send + Sync + 'static + Serialize,
AssetId: Codec + MaybeDisplay + core::fmt::Debug + Send + Sync + 'static + Serialize,
{
#[method(name = "services_queryServicesWithBlueprintsByOperator")]
fn query_services_with_blueprints_by_operator(
&self,
operator: AccountId,
at: Option<BlockHash>,
) -> RpcResult<Vec<RpcServicesWithBlueprint<X, AccountId, BlockNumber>>>;
) -> RpcResult<Vec<RpcServicesWithBlueprint<X, AccountId, BlockNumber, AssetId>>>;
}

/// A struct that implements the `ServicesApi`.
Expand All @@ -63,21 +64,22 @@ impl<C, M, P> ServicesClient<C, M, P> {
}
}

impl<C, X, Block, AccountId>
ServicesApiServer<<Block as BlockT>::Hash, X, AccountId, BlockNumberOf<Block>>
impl<C, X, Block, AccountId, AssetId>
ServicesApiServer<<Block as BlockT>::Hash, X, AccountId, BlockNumberOf<Block>, AssetId>
for ServicesClient<C, Block, AccountId>
where
Block: BlockT,
AccountId: Codec + MaybeDisplay + core::fmt::Debug + Send + Sync + 'static + Serialize,
AssetId: Codec + MaybeDisplay + core::fmt::Debug + Send + Sync + 'static + Serialize,
X: Constraints,
C: HeaderBackend<Block> + ProvideRuntimeApi<Block> + Send + Sync + 'static,
C::Api: ServicesRuntimeApi<Block, X, AccountId>,
C::Api: ServicesRuntimeApi<Block, X, AccountId, AssetId>,
{
fn query_services_with_blueprints_by_operator(
&self,
operator: AccountId,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<Vec<RpcServicesWithBlueprint<X, AccountId, BlockNumberOf<Block>>>> {
) -> RpcResult<Vec<RpcServicesWithBlueprint<X, AccountId, BlockNumberOf<Block>, AssetId>>> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

Expand Down
2 changes: 2 additions & 0 deletions pallets/services/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ impl<T: Config> Constraints for types::ConstraintsOf<T> {
type MaxContainerImageNameLength = T::MaxContainerImageNameLength;

type MaxContainerImageTagLength = T::MaxContainerImageTagLength;

type MaxAssetsPerService = T::MaxAssetsPerService;
}

impl traits::EvmGasWeightMapping for () {
Expand Down
41 changes: 38 additions & 3 deletions pallets/services/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub mod module {
use super::*;
use frame_support::dispatch::PostDispatchInfo;
use sp_core::{H160, H256};
use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize};
use sp_std::vec::Vec;
use tangle_primitives::{
services::{PriceTargets, *},
Expand Down Expand Up @@ -86,6 +87,16 @@ pub mod module {
/// Substrate weight and vice versa.
type EvmGasWeightMapping: traits::EvmGasWeightMapping;

/// The asset ID type.
type AssetId: AtLeast32BitUnsigned
+ Parameter
+ Member
+ MaybeSerializeDeserialize
+ Clone
+ Copy
+ PartialOrd
+ MaxEncodedLen;

/// Maximum number of fields in a job call.
#[pallet::constant]
type MaxFields: Get<u32> + Default + Parameter + MaybeSerializeDeserialize;
Expand Down Expand Up @@ -143,6 +154,9 @@ pub mod module {
/// Container image tag maximum length.
#[pallet::constant]
type MaxContainerImageTagLength: Get<u32> + Default + Parameter + MaybeSerializeDeserialize;
/// Maximum number of assets per service.
#[pallet::constant]
type MaxAssetsPerService: 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 @@ -208,6 +222,10 @@ pub mod module {
MaxServicesPerProviderExceeded,
/// The operator is not active, ensure operator status is ACTIVE in multi-asset-delegation
OperatorNotActive,
/// No assets provided for the service, at least one asset is required.
NoAssetsProvided,
/// The maximum number of assets per service has been exceeded.
MaxAssetsPerServiceExceeded,
}

#[pallet::event]
Expand Down Expand Up @@ -278,6 +296,8 @@ pub mod module {
pending_approvals: Vec<T::AccountId>,
/// The list of operators that atomaticaly approved the service.
approved: Vec<T::AccountId>,
/// The list of asset IDs that are being used to secure the service.
assets: Vec<T::AssetId>,
},
/// A service request has been approved.
ServiceRequestApproved {
Expand Down Expand Up @@ -325,6 +345,8 @@ pub mod module {
service_id: u64,
/// The ID of the service blueprint.
blueprint_id: u64,
/// The list of asset IDs that are being used to secure the service.
assets: Vec<T::AssetId>,
},

/// A service has been terminated.
Expand Down Expand Up @@ -436,7 +458,7 @@ pub mod module {
_,
Identity,
u64,
ServiceRequest<T::Constraints, T::AccountId, BlockNumberFor<T>>,
ServiceRequest<T::Constraints, T::AccountId, BlockNumberFor<T>, T::AssetId>,
ResultQuery<Error<T>::ServiceRequestNotFound>,
>;

Expand All @@ -448,7 +470,7 @@ pub mod module {
_,
Identity,
u64,
Service<T::Constraints, T::AccountId, BlockNumberFor<T>>,
Service<T::Constraints, T::AccountId, BlockNumberFor<T>, T::AssetId>,
ResultQuery<Error<T>::ServiceNotFound>,
>;

Expand Down Expand Up @@ -710,14 +732,18 @@ pub mod module {
#[pallet::compact] blueprint_id: u64,
permitted_callers: Vec<T::AccountId>,
service_providers: Vec<T::AccountId>,
#[pallet::compact] ttl: BlockNumberFor<T>,
request_args: Vec<Field<T::Constraints, T::AccountId>>,
assets: Vec<T::AssetId>,
#[pallet::compact] ttl: BlockNumberFor<T>,
) -> DispatchResultWithPostInfo {
// TODO(@shekohex): split this function into smaller functions.
let caller = ensure_signed(origin)?;
let (_, blueprint) = Self::blueprints(blueprint_id)?;

blueprint.type_check_request(&request_args).map_err(Error::<T>::TypeCheck)?;
// ensure we at least have one asset
ensure!(!assets.is_empty(), Error::<T>::NoAssetsProvided);

let mut preferences = Vec::new();
let mut pending_approvals = Vec::new();
let mut approved = Vec::new();
Expand All @@ -740,6 +766,8 @@ pub mod module {
let permitted_callers =
BoundedVec::<_, MaxPermittedCallersOf<T>>::try_from(permitted_callers)
.map_err(|_| Error::<T>::MaxPermittedCallersExceeded)?;
let assets = BoundedVec::<_, MaxAssetsPerServiceOf<T>>::try_from(assets)
.map_err(|_| Error::<T>::MaxAssetsPerServiceExceeded)?;
if pending_approvals.is_empty() {
// No approval is required, initiate the service immediately.
for operator in &approved {
Expand All @@ -757,6 +785,7 @@ pub mod module {
id: service_id,
blueprint: blueprint_id,
owner: caller.clone(),
assets: assets.clone(),
permitted_callers,
operators,
ttl,
Expand All @@ -774,6 +803,7 @@ pub mod module {
request_id: None,
service_id,
blueprint_id,
assets: assets.to_vec(),
});

// TODO: add weight for the request to the total weight.
Expand All @@ -793,9 +823,11 @@ pub mod module {
let operators_with_approval_state =
BoundedVec::<_, MaxOperatorsPerServiceOf<T>>::try_from(operators)
.map_err(|_| Error::<T>::MaxServiceProvidersExceeded)?;

let service_request = ServiceRequest {
blueprint: blueprint_id,
owner: caller.clone(),
assets: assets.clone(),
ttl,
args,
permitted_callers,
Expand All @@ -810,6 +842,7 @@ pub mod module {
blueprint_id,
pending_approvals,
approved,
assets: assets.to_vec(),
});

// TODO: add weight for the request to the total weight.
Expand Down Expand Up @@ -879,6 +912,7 @@ pub mod module {
id: service_id,
blueprint: request.blueprint,
owner: request.owner.clone(),
assets: request.assets.clone(),
permitted_callers: request.permitted_callers.clone(),
operators,
ttl: request.ttl,
Expand All @@ -895,6 +929,7 @@ pub mod module {
Self::deposit_event(Event::ServiceInitiated {
owner: request.owner,
request_id: Some(request_id),
assets: request.assets.to_vec(),
service_id,
blueprint_id: request.blueprint,
});
Expand Down
10 changes: 9 additions & 1 deletion pallets/services/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,13 @@ impl EvmGasWeightMapping for PalletEVMGasWeightMapping {
}
}

pub type AssetId = u32;

pub struct MockDelegationManager;
impl tangle_primitives::traits::MultiAssetDelegationInfo<AccountId, Balance>
for MockDelegationManager
{
type AssetId = u32;
type AssetId = AssetId;

fn get_current_round() -> tangle_primitives::types::RoundIndex {
Default::default()
Expand Down Expand Up @@ -329,13 +331,18 @@ parameter_types! {
#[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub const MaxContainerImageTagLength: u32 = 1024;

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

impl Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type ForceOrigin = frame_system::EnsureRoot<AccountId>;
type Currency = Balances;
type PalletId = ServicesPalletId;
type AssetId = AssetId;
type EvmRunner = MockedEvmRunner;
type EvmGasWeightMapping = PalletEVMGasWeightMapping;
type MaxFields = MaxFields;
Expand All @@ -357,6 +364,7 @@ impl Config for Runtime {
type MaxContainerRegistryLength = MaxContainerRegistryLength;
type MaxContainerImageNameLength = MaxContainerImageNameLength;
type MaxContainerImageTagLength = MaxContainerImageTagLength;
type MaxAssetsPerService = MaxAssetsPerService;
type Constraints = pallet_services::types::ConstraintsOf<Self>;
type OperatorDelegationManager = MockDelegationManager;
type WeightInfo = ();
Expand Down
2 changes: 1 addition & 1 deletion pallets/services/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ impl<T: Config> Pallet<T> {
pub fn services_with_blueprints_by_operator(
operator: T::AccountId,
) -> Result<
Vec<RpcServicesWithBlueprint<T::Constraints, T::AccountId, BlockNumberFor<T>>>,
Vec<RpcServicesWithBlueprint<T::Constraints, T::AccountId, BlockNumberFor<T>, T::AssetId>>,
Error<T>,
> {
let profile = Self::operator_profile(operator)?;
Expand Down
Loading

0 comments on commit 86e098d

Please sign in to comment.