@@ -59,6 +59,14 @@ pub struct SystemBundle {
5959 pub metadata : SystemBundleMetadata ,
6060}
6161
62+ #[ derive( Debug , thiserror:: Error ) ]
63+ pub enum SystemBundleDecodingError {
64+ #[ error( transparent) ]
65+ RawBundleConvertError ( #[ from] RawBundleConvertError ) ,
66+ #[ error( "bundle contains too many transactions" ) ]
67+ TooManyTransactions ,
68+ }
69+
6270/// Decoded bundle type. Either a new, full bundle or an empty replacement bundle.
6371#[ allow( clippy:: large_enum_variant) ]
6472#[ derive( PartialEq , Eq , Clone , Debug ) ]
@@ -241,33 +249,56 @@ impl BundleHash for RawShareBundle {
241249 }
242250}
243251
244- impl SystemBundle {
252+ /// Decoder for system bundles with additional constraints.
253+ #[ derive( Debug , Clone , Copy ) ]
254+ pub struct SystemBundleDecoder {
255+ /// Maximum number of transactions allowed in a bundle.
256+ pub max_txs_per_bundle : usize ,
257+ }
258+
259+ impl Default for SystemBundleDecoder {
260+ fn default ( ) -> Self {
261+ Self { max_txs_per_bundle : Self :: DEFAULT_MAX_TXS_PER_BUNDLE }
262+ }
263+ }
264+
265+ impl SystemBundleDecoder {
266+ /// The maximum number of transactions allowed in a bundle received via `eth_sendBundle`.
267+ pub const DEFAULT_MAX_TXS_PER_BUNDLE : usize = 100 ;
268+
245269 /// Create a new system bundle from a raw bundle and additional data.
246270 /// Returns an error if the raw bundle fails to decode.
247271 pub fn try_decode (
272+ & self ,
248273 bundle : RawBundle ,
249274 metadata : SystemBundleMetadata ,
250- ) -> Result < Self , RawBundleConvertError > {
251- Self :: try_decode_inner ( bundle, metadata, None :: < fn ( B256 ) -> Option < Address > > )
275+ ) -> Result < SystemBundle , SystemBundleDecodingError > {
276+ self . try_decode_inner ( bundle, metadata, None :: < fn ( B256 ) -> Option < Address > > )
252277 }
253278
254279 /// Create a new system bundle from a raw bundle and additional data, using a signer lookup
255280 /// function for the transaction signers.
256281 pub fn try_decode_with_lookup (
282+ & self ,
257283 bundle : RawBundle ,
258284 metadata : SystemBundleMetadata ,
259285 lookup : impl Fn ( B256 ) -> Option < Address > ,
260- ) -> Result < Self , RawBundleConvertError > {
261- Self :: try_decode_inner ( bundle, metadata, Some ( lookup) )
286+ ) -> Result < SystemBundle , SystemBundleDecodingError > {
287+ self . try_decode_inner ( bundle, metadata, Some ( lookup) )
262288 }
263289
264290 /// Create a new system bundle from a raw bundle and additional data, using a signer lookup
265291 /// function for the transaction signers. Returns an error if the raw bundle fails to decode.
266292 fn try_decode_inner (
293+ & self ,
267294 mut bundle : RawBundle ,
268295 metadata : SystemBundleMetadata ,
269296 lookup : Option < impl Fn ( B256 ) -> Option < Address > > ,
270- ) -> Result < Self , RawBundleConvertError > {
297+ ) -> Result < SystemBundle , SystemBundleDecodingError > {
298+ if bundle. txs . len ( ) > self . max_txs_per_bundle {
299+ return Err ( SystemBundleDecodingError :: TooManyTransactions ) ;
300+ }
301+
271302 let raw_bundle_hash = bundle. bundle_hash ( ) ;
272303 // Set the bundle hash in the metadata.
273304 bundle. metadata . bundle_hash = Some ( raw_bundle_hash) ;
@@ -282,14 +313,16 @@ impl SystemBundle {
282313 bundle. signer = Some ( metadata. signer ) ;
283314 }
284315
285- Ok ( Self {
316+ Ok ( SystemBundle {
286317 raw_bundle : Arc :: new ( bundle) ,
287318 decoded_bundle : Arc :: new ( decoded) ,
288319 bundle_hash : raw_bundle_hash,
289320 metadata,
290321 } )
291322 }
323+ }
292324
325+ impl SystemBundle {
293326 /// Returns `true` if the bundle is a replacement.
294327 pub fn is_replacement ( & self ) -> bool {
295328 matches ! ( self . decoded_bundle. as_ref( ) , DecodedBundle :: EmptyReplacement ( _) )
@@ -761,4 +794,39 @@ mod tests {
761794 let json = serde_json:: to_value ( response) . unwrap ( ) ;
762795 assert_eq ! ( json, json!( hash) ) ;
763796 }
797+
798+ #[ test]
799+ fn too_many_txs_error ( ) {
800+ let decoder = SystemBundleDecoder :: default ( ) ;
801+ let raw_bundle = RawBundle {
802+ txs : vec ! [ Bytes :: from( vec![ 0u8 ; 8 ] ) ; decoder. max_txs_per_bundle + 1 ] ,
803+ metadata : RawBundleMetadata {
804+ version : None ,
805+ block_number : None ,
806+ reverting_tx_hashes : vec ! [ ] ,
807+ dropping_tx_hashes : vec ! [ ] ,
808+ replacement_uuid : None ,
809+ uuid : None ,
810+ signing_address : None ,
811+ refund_identity : None ,
812+ min_timestamp : None ,
813+ max_timestamp : None ,
814+ replacement_nonce : None ,
815+ refund_percent : None ,
816+ refund_recipient : None ,
817+ refund_tx_hashes : None ,
818+ delayed_refund : None ,
819+ bundle_hash : None ,
820+ } ,
821+ } ;
822+ let metadata = SystemBundleMetadata {
823+ signer : Address :: ZERO ,
824+ received_at : UtcInstant :: now ( ) ,
825+ priority : Priority :: Medium ,
826+ } ;
827+
828+ // This should be the first reason decoding of such garbage data fails.
829+ let result = decoder. try_decode ( raw_bundle. clone ( ) , metadata. clone ( ) ) ;
830+ assert ! ( matches!( result, Err ( SystemBundleDecodingError :: TooManyTransactions ) ) ) ;
831+ }
764832}
0 commit comments