@@ -14,6 +14,7 @@ use builder::test_utils::{
1414} ;
1515use signet_bundle:: RecoveredBundle ;
1616use signet_sim:: { BuiltBlock , SimCache } ;
17+ use std:: collections:: HashSet ;
1718use std:: time:: Duration ;
1819
1920/// Block number used for all test environments and bundles.
@@ -25,6 +26,9 @@ const BLOCK_TIMESTAMP: u64 = 1_700_000_000;
2526/// Parmigiana rollup chain ID.
2627const RU_CHAIN_ID : u64 = 88888 ;
2728
29+ /// Default max priority fee used for transfer bundles in tests.
30+ const DEFAULT_PRIORITY_FEE : u128 = 10_000_000_000 ;
31+
2832/// Generate N random funded signers and a database builder with all of them funded.
2933fn generate_funded_accounts ( n : usize ) -> ( Vec < PrivateKeySigner > , TestDbBuilder ) {
3034 let signers: Vec < PrivateKeySigner > = ( 0 ..n) . map ( |_| PrivateKeySigner :: random ( ) ) . collect ( ) ;
@@ -39,16 +43,14 @@ fn generate_funded_accounts(n: usize) -> (Vec<PrivateKeySigner>, TestDbBuilder)
3943}
4044
4145/// Create a `RecoveredBundle` with one transfer transaction.
42- fn make_bundle ( signer : & PrivateKeySigner , to : Address , uuid : String ) -> RecoveredBundle {
43- let tx = create_transfer_tx (
44- signer,
45- to,
46- U256 :: from ( 1_000u64 ) ,
47- 0 ,
48- RU_CHAIN_ID ,
49- 10_000_000_000 , // 10 gwei priority fee
50- )
51- . unwrap ( ) ;
46+ fn make_bundle (
47+ signer : & PrivateKeySigner ,
48+ to : Address ,
49+ uuid : String ,
50+ max_priority_fee : u128 ,
51+ ) -> RecoveredBundle {
52+ let tx = create_transfer_tx ( signer, to, U256 :: from ( 1_000u64 ) , 0 , RU_CHAIN_ID , max_priority_fee)
53+ . unwrap ( ) ;
5254
5355 RecoveredBundle :: new_unchecked (
5456 vec ! [ tx] ,
@@ -89,7 +91,9 @@ async fn test_load_many_bundles() {
8991 let bundles: Vec < RecoveredBundle > = signers
9092 . iter ( )
9193 . enumerate ( )
92- . map ( |( i, signer) | make_bundle ( signer, recipient, format ! ( "bundle-{i}" ) ) )
94+ . map ( |( i, signer) | {
95+ make_bundle ( signer, recipient, format ! ( "bundle-{i}" ) , DEFAULT_PRIORITY_FEE )
96+ } )
9397 . collect ( ) ;
9498
9599 cache. add_bundles ( bundles, DEFAULT_BASEFEE ) ;
@@ -118,7 +122,10 @@ async fn test_load_50k_bundles() {
118122 let bundles: Vec < RecoveredBundle > = signers
119123 . iter ( )
120124 . enumerate ( )
121- . map ( |( i, signer) | make_bundle ( signer, recipient, format ! ( "bundle-{i}" ) ) )
125+ . map ( |( i, signer) | {
126+ // Keep ranks distinct to avoid pathological cache insertion cost at high volume.
127+ make_bundle ( signer, recipient, format ! ( "bundle-{i}" ) , DEFAULT_PRIORITY_FEE + i as u128 )
128+ } )
122129 . collect ( ) ;
123130
124131 cache. add_bundles ( bundles, DEFAULT_BASEFEE ) ;
@@ -145,7 +152,9 @@ async fn test_load_bundles_and_txs_mixed() {
145152 let bundles: Vec < RecoveredBundle > = signers[ ..bundle_count]
146153 . iter ( )
147154 . enumerate ( )
148- . map ( |( i, signer) | make_bundle ( signer, recipient, format ! ( "mix-bundle-{i}" ) ) )
155+ . map ( |( i, signer) | {
156+ make_bundle ( signer, recipient, format ! ( "mix-bundle-{i}" ) , DEFAULT_PRIORITY_FEE )
157+ } )
149158 . collect ( ) ;
150159 cache. add_bundles ( bundles, DEFAULT_BASEFEE ) ;
151160
@@ -187,7 +196,9 @@ async fn test_load_saturate_gas_limit() {
187196 let bundles: Vec < RecoveredBundle > = signers
188197 . iter ( )
189198 . enumerate ( )
190- . map ( |( i, signer) | make_bundle ( signer, recipient, format ! ( "gas-bundle-{i}" ) ) )
199+ . map ( |( i, signer) | {
200+ make_bundle ( signer, recipient, format ! ( "gas-bundle-{i}" ) , DEFAULT_PRIORITY_FEE )
201+ } )
191202 . collect ( ) ;
192203 cache. add_bundles ( bundles, DEFAULT_BASEFEE ) ;
193204
@@ -219,7 +230,9 @@ async fn test_load_deadline_pressure() {
219230 let bundles: Vec < RecoveredBundle > = signers
220231 . iter ( )
221232 . enumerate ( )
222- . map ( |( i, signer) | make_bundle ( signer, recipient, format ! ( "deadline-bundle-{i}" ) ) )
233+ . map ( |( i, signer) | {
234+ make_bundle ( signer, recipient, format ! ( "deadline-bundle-{i}" ) , DEFAULT_PRIORITY_FEE )
235+ } )
223236 . collect ( ) ;
224237 cache. add_bundles ( bundles, DEFAULT_BASEFEE ) ;
225238
@@ -236,3 +249,60 @@ async fn test_load_deadline_pressure() {
236249 // Should complete within a reasonable margin of the deadline.
237250 assert ! ( elapsed < deadline * 3 , "block build took {elapsed:?}, expected within ~{deadline:?}" ) ;
238251}
252+
253+ /// Gas-constrained block: verify the builder selects the highest-fee bundles first.
254+ ///
255+ /// 10 low-fee bundles + 10 high-fee bundles, with a gas cap that can only fit 10.
256+ /// Every included transaction must originate from a high-fee sender.
257+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 4 ) ]
258+ async fn test_load_fee_priority_ordering ( ) {
259+ let low_count = 10usize ;
260+ let high_count = 10usize ;
261+ let total = low_count + high_count;
262+
263+ let ( signers, db_builder) = generate_funded_accounts ( total) ;
264+ let recipient = Address :: repeat_byte ( 0xEE ) ;
265+
266+ let cache = SimCache :: with_capacity ( total) ;
267+
268+ let low_fee = DEFAULT_PRIORITY_FEE ;
269+ let high_fee = 90_000_000_000u128 ; // 90 Gwei — valid (<= max_fee_per_gas=100 Gwei), 9× above low_fee
270+
271+ let low_fee_senders: HashSet < Address > =
272+ signers[ ..low_count] . iter ( ) . map ( |s| s. address ( ) ) . collect ( ) ;
273+
274+ let bundles: Vec < RecoveredBundle > = signers
275+ . iter ( )
276+ . enumerate ( )
277+ . map ( |( i, signer) | {
278+ let fee = if i < low_count { low_fee } else { high_fee } ;
279+ make_bundle ( signer, recipient, format ! ( "priority-bundle-{i}" ) , fee)
280+ } )
281+ . collect ( ) ;
282+
283+ cache. add_bundles ( bundles, DEFAULT_BASEFEE ) ;
284+
285+ // Gas limit exactly fits the 10 high-fee bundles (21,000 gas each).
286+ let max_gas: u64 = 21_000 * high_count as u64 ;
287+
288+ let builder = build_env ( db_builder)
289+ . with_cache ( cache)
290+ . with_deadline ( Duration :: from_secs ( 5 ) )
291+ . with_max_gas ( max_gas) ;
292+ let built: BuiltBlock = builder. build ( ) . build ( ) . await ;
293+
294+ assert_eq ! (
295+ built. tx_count( ) ,
296+ high_count,
297+ "expected exactly {high_count} txs, got {}" ,
298+ built. tx_count( )
299+ ) ;
300+
301+ for tx in built. transactions ( ) {
302+ assert ! (
303+ !low_fee_senders. contains( & tx. signer( ) ) ,
304+ "low-fee sender {} was included instead of a high-fee sender" ,
305+ tx. signer( )
306+ ) ;
307+ }
308+ }
0 commit comments