@@ -14,6 +14,17 @@ mod tests;
14
14
mod benchmarking;
15
15
pub mod weights;
16
16
17
+ use codec:: { Decode , Encode } ;
18
+ use scale_info:: TypeInfo ;
19
+ use sp_runtime:: {
20
+ traits:: { DispatchInfoOf , SignedExtension } ,
21
+ transaction_validity:: {
22
+ InvalidTransaction , TransactionValidity , TransactionValidityError , ValidTransaction ,
23
+ } ,
24
+ } ;
25
+
26
+ use frame_support:: traits:: { Get , IsSubType } ;
27
+
17
28
#[ frame_support:: pallet]
18
29
pub mod pallet {
19
30
pub use crate :: weights:: WeightInfo ;
@@ -153,7 +164,10 @@ pub mod pallet {
153
164
//
154
165
// To the submit_blob weight is added a flat cost relative to the on_finalize execution
155
166
// the amount is equal to the entire weight of on_finalized divided by 1/4 of the MaxBlobs
156
- // this covers perfectly the on_finalize cost if on avarage 1/4 of the possible blobs are submitted in one block
167
+ // this covers perfectly the on_finalize cost if on average 1/4 of the possible blobs are submitted in one block
168
+ //
169
+ // Note: this PANICS if the size of the blob, the total size of all blobs, or the total number of blobs submitted
170
+ // exceed their respective configured limits. These panics are intended to be protected against by the [`crate::PrevalidateBlobs`] extension.
157
171
#[ pallet:: weight(
158
172
T :: WeightInfo :: submit_blob( T :: MaxBlobs :: get( ) / 2 , blob. len( ) as u32 )
159
173
. saturating_add( T :: WeightInfo :: on_finalize( 0 ) / ( T :: MaxBlobs :: get( ) / 4 ) as u64 )
@@ -165,20 +179,25 @@ pub mod pallet {
165
179
) -> DispatchResultWithPostInfo {
166
180
let who = ensure_signed ( origin) ?;
167
181
182
+ let blob_len = blob. len ( ) as u32 ;
183
+ if blob_len > T :: MaxBlobSize :: get ( ) {
184
+ panic ! ( "Blob size limit exceeded" ) ;
185
+ }
186
+
168
187
let Some ( extrinsic_index) = <frame_system:: Pallet < T > >:: extrinsic_index ( ) else {
169
188
return Err ( Error :: < T > :: NoExtrinsicIndex . into ( ) ) ;
170
189
} ;
171
190
172
191
let total_blobs = TotalBlobs :: < T > :: get ( ) ;
173
192
if total_blobs + 1 > T :: MaxBlobs :: get ( ) {
174
- return Err ( Error :: < T > :: MaxBlobsReached . into ( ) ) ;
193
+ panic ! ( "Maximum blob limit exceeded" ) ;
175
194
}
176
195
TotalBlobs :: < T > :: put ( total_blobs + 1 ) ;
177
196
178
197
let blob_len = blob. len ( ) as u32 ;
179
198
let total_blobs_size = TotalBlobsSize :: < T > :: get ( ) ;
180
199
if total_blobs_size + blob_len > T :: MaxTotalBlobSize :: get ( ) {
181
- return Err ( Error :: < T > :: MaxTotalBlobsSizeReached . into ( ) ) ;
200
+ panic ! ( "Maximum total blob size exceeded" ) ;
182
201
}
183
202
TotalBlobsSize :: < T > :: put ( total_blobs_size + blob_len) ;
184
203
@@ -208,3 +227,96 @@ pub mod pallet {
208
227
sha2:: Sha256 :: digest ( data) . into ( )
209
228
}
210
229
}
230
+
231
+ /// Prevalidates blob limits
232
+ #[ derive( Encode , Decode , Clone , Eq , PartialEq , TypeInfo ) ]
233
+ #[ scale_info( skip_type_params( T ) ) ]
234
+ pub struct PrevalidateBlobs < T > ( sp_std:: marker:: PhantomData < T > ) ;
235
+
236
+ impl < T > PrevalidateBlobs < T > {
237
+ /// Create new `SignedExtension` to prevalidate blob sizes.
238
+ pub fn new ( ) -> Self {
239
+ Self ( sp_std:: marker:: PhantomData )
240
+ }
241
+ }
242
+
243
+ impl < T > sp_std:: fmt:: Debug for PrevalidateBlobs < T > {
244
+ #[ cfg( feature = "std" ) ]
245
+ fn fmt ( & self , f : & mut sp_std:: fmt:: Formatter ) -> sp_std:: fmt:: Result {
246
+ write ! ( f, "PrevalidateBlobs" )
247
+ }
248
+
249
+ #[ cfg( not( feature = "std" ) ) ]
250
+ fn fmt ( & self , _: & mut sp_std:: fmt:: Formatter ) -> sp_std:: fmt:: Result {
251
+ Ok ( ( ) )
252
+ }
253
+ }
254
+
255
+ impl < T : Config + Send + Sync > SignedExtension for PrevalidateBlobs < T >
256
+ where
257
+ <T as frame_system:: Config >:: RuntimeCall : IsSubType < Call < T > > ,
258
+ {
259
+ type AccountId = T :: AccountId ;
260
+ type Call = <T as frame_system:: Config >:: RuntimeCall ;
261
+ type AdditionalSigned = ( ) ;
262
+ type Pre = ( ) ;
263
+ const IDENTIFIER : & ' static str = "PrevalidateBlobs" ;
264
+
265
+ fn additional_signed ( & self ) -> Result < Self :: AdditionalSigned , TransactionValidityError > {
266
+ Ok ( ( ) )
267
+ }
268
+
269
+ fn pre_dispatch (
270
+ self ,
271
+ who : & Self :: AccountId ,
272
+ call : & Self :: Call ,
273
+ info : & DispatchInfoOf < Self :: Call > ,
274
+ len : usize ,
275
+ ) -> Result < Self :: Pre , TransactionValidityError > {
276
+ // This is what's called prior to dispatching within an actual block.
277
+
278
+ // Check individual blob size limits
279
+ self . validate ( who, call, info, len) ?;
280
+
281
+ // Here, we return `ExhaustsResources` if the total amount or size of blobs
282
+ // within a block is disrespected.
283
+ //
284
+ // This will cause honest nodes authoring blocks to skip the transaction without
285
+ // expunging it from their transaction pool.
286
+ if let Some ( local_call) = call. is_sub_type ( ) {
287
+ if let Call :: submit_blob { blob, .. } = local_call {
288
+ if TotalBlobs :: < T > :: get ( ) + 1 > T :: MaxBlobs :: get ( ) {
289
+ return Err ( InvalidTransaction :: ExhaustsResources . into ( ) ) ;
290
+ }
291
+
292
+ if TotalBlobsSize :: < T > :: get ( ) + blob. len ( ) as u32 > T :: MaxTotalBlobSize :: get ( ) {
293
+ return Err ( InvalidTransaction :: ExhaustsResources . into ( ) ) ;
294
+ }
295
+ }
296
+ }
297
+
298
+ Ok ( ( ) )
299
+ }
300
+
301
+ fn validate (
302
+ & self ,
303
+ _who : & Self :: AccountId ,
304
+ call : & Self :: Call ,
305
+ _info : & DispatchInfoOf < Self :: Call > ,
306
+ _len : usize ,
307
+ ) -> TransactionValidity {
308
+ // This is what's called when evaluating transactions within the pool.
309
+
310
+ if let Some ( local_call) = call. is_sub_type ( ) {
311
+ if let Call :: submit_blob { blob, .. } = local_call {
312
+ if blob. len ( ) as u32 > T :: MaxBlobSize :: get ( ) {
313
+ // This causes the transaction to be expunged from the transaction pool.
314
+ // It will not be valid unless the configured limit is increased by governance,
315
+ // which is a rare event.
316
+ return Err ( InvalidTransaction :: Custom ( 0 ) . into ( ) ) ;
317
+ }
318
+ }
319
+ }
320
+ Ok ( ValidTransaction :: default ( ) )
321
+ }
322
+ }
0 commit comments