@@ -23,7 +23,6 @@ use frame_support::{
2323 dispatch:: { DispatchResult , DispatchResultWithPostInfo , Pays , PostDispatchInfo } ,
2424 ensure,
2525 pallet_prelude:: * ,
26- storage:: TransactionOutcome ,
2726 traits:: {
2827 Currency , ReservableCurrency ,
2928 fungibles:: { Inspect , Mutate } ,
@@ -42,6 +41,7 @@ use tangle_primitives::{
4241} ;
4342
4443#[ cfg( not( feature = "std" ) ) ]
44+ #[ allow( unused_imports) ]
4545use alloc:: string:: String ;
4646
4747pub mod functions;
@@ -247,6 +247,22 @@ pub mod module {
247247 + Parameter
248248 + MaybeSerializeDeserialize ;
249249
250+ /// Maximum number of slashes to process per block to prevent DoS attacks.
251+ #[ pallet:: constant]
252+ type MaxSlashesPerBlock : Get < u32 > + Default + Parameter + MaybeSerializeDeserialize ;
253+
254+ /// Maximum size of metrics data in heartbeat messages (in bytes).
255+ #[ pallet:: constant]
256+ type MaxMetricsDataSize : Get < u32 > + Default + Parameter + MaybeSerializeDeserialize ;
257+
258+ /// Fallback weight for reads when weight calculation overflows.
259+ #[ pallet:: constant]
260+ type FallbackWeightReads : Get < u64 > + Default + Parameter + MaybeSerializeDeserialize ;
261+
262+ /// Fallback weight for writes when weight calculation overflows.
263+ #[ pallet:: constant]
264+ type FallbackWeightWrites : Get < u64 > + Default + Parameter + MaybeSerializeDeserialize ;
265+
250266 /// Weight information for the extrinsics in this module.
251267 type WeightInfo : WeightInfo ;
252268 }
@@ -267,37 +283,38 @@ pub mod module {
267283 /// On initialize, we should check for any unapplied slashes and apply them.
268284 /// Also process subscription payments for active services.
269285 fn on_initialize ( n : BlockNumberFor < T > ) -> Weight {
270- let mut weight = Zero :: zero ( ) ;
286+ let mut weight: Weight = Weight :: zero ( ) ;
271287 let current_era = T :: OperatorDelegationManager :: get_current_round ( ) ;
272288 let slash_defer_duration = T :: SlashDeferDuration :: get ( ) ;
273289
274290 // Only process slashes from eras that have completed their deferral period
275291 let process_era = current_era. saturating_sub ( slash_defer_duration) ;
276292
293+ // Limit processing to prevent DoS attacks using configurable limit
294+ let max_slashes_per_block = T :: MaxSlashesPerBlock :: get ( ) ;
295+
277296 // Get all unapplied slashes for this era
278297 let prefix_iter = UnappliedSlashes :: < T > :: iter_prefix ( process_era) ;
279- for ( index, slash) in prefix_iter {
280- // TODO: This call must be all or nothing.
281- // TODO: If fail then revert all storage changes
282- if Self :: slashing_enabled ( ) {
283- let _ = frame_support:: storage:: with_transaction (
284- || -> TransactionOutcome < Result < _ , DispatchError > > {
285- let res = T :: SlashManager :: slash_operator ( & slash) ;
286- match & res {
287- Ok ( weight_used) => {
288- weight =
289- weight_used. checked_add ( & weight) . unwrap_or_else ( Zero :: zero) ;
290- // Remove the slash from storage after successful application
291- UnappliedSlashes :: < T > :: remove ( process_era, index) ;
292- TransactionOutcome :: Commit ( Ok ( res) )
293- } ,
294- Err ( _) => {
295- log:: error!( "Failed to apply slash for index: {:?}" , index) ;
296- TransactionOutcome :: Rollback ( Ok ( res) )
297- } ,
298- }
299- } ,
300- ) ;
298+
299+ for ( processed_slashes, ( index, slash) ) in prefix_iter. enumerate ( ) {
300+ if processed_slashes >= max_slashes_per_block as usize {
301+ break ;
302+ }
303+
304+ let res = T :: SlashManager :: slash_operator ( & slash) ;
305+ match & res {
306+ Ok ( weight_used) => {
307+ weight = weight. checked_add ( weight_used) . unwrap_or (
308+ T :: DbWeight :: get ( ) . reads_writes (
309+ T :: FallbackWeightReads :: get ( ) ,
310+ T :: FallbackWeightWrites :: get ( ) ,
311+ ) ,
312+ ) ;
313+ UnappliedSlashes :: < T > :: remove ( process_era, index) ;
314+ } ,
315+ Err ( _) => {
316+ weight = weight. saturating_add ( T :: DbWeight :: get ( ) . reads_writes ( 1 , 0 ) ) ;
317+ } ,
301318 }
302319 }
303320
@@ -491,6 +508,14 @@ pub mod module {
491508 TooManySubscriptions ,
492509 /// Custom asset transfer failed
493510 CustomAssetTransferFailed ,
511+ /// Invalid event count provided
512+ InvalidEventCount ,
513+ /// Metrics data too large
514+ MetricsDataTooLarge ,
515+ /// Subscription not valid
516+ SubscriptionNotValid ,
517+ /// Service not owned by caller
518+ ServiceNotOwned ,
494519 }
495520
496521 #[ pallet:: event]
@@ -1151,27 +1176,31 @@ pub mod module {
11511176 registration_args : Vec < Field < T :: Constraints , T :: AccountId > > ,
11521177 #[ pallet:: compact] value : BalanceOf < T > ,
11531178 ) -> DispatchResultWithPostInfo {
1154- let caller = ensure_signed ( origin) ?;
1155- // Validate the operator preferences
1156- ensure ! ( preferences. key != [ 0u8 ; 65 ] , Error :: <T >:: InvalidKey ) ;
1157- // Check if the caller is an active operator in the delegation system
1179+ let operator = ensure_signed ( origin) ?;
1180+
1181+ // Ensure operator is active in delegation system
11581182 ensure ! (
1159- T :: OperatorDelegationManager :: is_operator_active( & caller ) ,
1183+ T :: OperatorDelegationManager :: is_operator_active( & operator ) ,
11601184 Error :: <T >:: OperatorNotActive
11611185 ) ;
1186+
1187+ // Validate the operator preferences
1188+ ensure ! ( preferences. key != [ 0u8 ; 65 ] , Error :: <T >:: InvalidKey ) ;
1189+
11621190 // Check if operator is already registered for this blueprint
11631191 ensure ! (
1164- !Operators :: <T >:: contains_key( blueprint_id, & caller ) ,
1192+ !Operators :: <T >:: contains_key( blueprint_id, & operator ) ,
11651193 Error :: <T >:: AlreadyRegistered
11661194 ) ;
1195+
11671196 // Check if the key is already in use
11681197 for ( _, prefs) in Operators :: < T > :: iter_prefix ( blueprint_id) {
11691198 if prefs. key == preferences. key {
11701199 return Err ( Error :: < T > :: DuplicateKey . into ( ) ) ;
11711200 }
11721201 }
11731202
1174- Self :: do_register ( & caller , blueprint_id, preferences, registration_args, value) ?;
1203+ Self :: do_register ( & operator , blueprint_id, preferences, registration_args, value) ?;
11751204 Ok ( PostDispatchInfo { actual_weight : None , pays_fee : Pays :: Yes } )
11761205 }
11771206
@@ -1661,6 +1690,7 @@ pub mod module {
16611690 ) -> DispatchResultWithPostInfo {
16621691 let caller = ensure_signed ( origin) ?;
16631692 let service = Self :: services ( service_id) ?;
1693+ let ( _, _blueprint) = Self :: blueprints ( service. blueprint ) ?;
16641694 let ( maybe_slashing_origin, _used_weight) = Self :: query_slashing_origin ( & service) ?;
16651695 let slashing_origin = maybe_slashing_origin. ok_or ( Error :: < T > :: NoSlashingOrigin ) ?;
16661696 ensure ! ( slashing_origin == caller, DispatchError :: BadOrigin ) ;
@@ -2081,6 +2111,10 @@ pub mod module {
20812111 ) -> DispatchResultWithPostInfo {
20822112 let caller = ensure_signed ( origin) ?;
20832113
2114+ // Validate metrics data size before processing
2115+ let max_metrics_size = T :: MaxMetricsDataSize :: get ( ) as usize ;
2116+ ensure ! ( metrics_data. len( ) <= max_metrics_size, Error :: <T >:: MetricsDataTooLarge ) ;
2117+
20842118 // Ensure the service exists and is active
20852119 ensure ! (
20862120 ServiceStatus :: <T >:: contains_key( blueprint_id, service_id) ,
@@ -2106,9 +2140,10 @@ pub mod module {
21062140 . map_err ( |_| Error :: < T > :: InvalidHeartbeatData ) ?;
21072141
21082142 // Create the message to verify
2109- // Format: service_id + blueprint_id + metrics_data
2143+ // Format: service_id + blueprint_id + current_block + metrics_data
21102144 let mut message = service_id. to_le_bytes ( ) . to_vec ( ) ;
21112145 message. extend_from_slice ( & blueprint_id. to_le_bytes ( ) ) ;
2146+ message. extend_from_slice ( & current_block. saturated_into :: < u64 > ( ) . to_le_bytes ( ) ) ;
21122147 message. extend_from_slice ( & bounded_metrics_data) ;
21132148 let message_hash = sp_io:: hashing:: keccak_256 ( & message) ;
21142149
0 commit comments