11use std:: collections:: BTreeMap ;
22
3+ use rand:: { Rng , SeedableRng } ;
4+ use rand_chacha:: ChaCha8Rng ;
35use rstest:: rstest;
46use starknet_api:: block:: { BlockNumber , GasPrice } ;
57
@@ -8,6 +10,11 @@ use crate::snip35::{
810 compute_fee_proposal,
911 compute_fee_target,
1012 FeeProposalInfo ,
13+ FEE_PROPOSAL_MARGIN_PPT ,
14+ ORACLE_L2_GAS_FLOOR_MAX_FRI ,
15+ ORACLE_L2_GAS_FLOOR_MIN_FRI ,
16+ PPT_DENOMINATOR ,
17+ TARGET_ATTO_USD_PER_L2_GAS ,
1118} ;
1219
1320#[ test]
@@ -149,8 +156,8 @@ fn test_compute_fee_proposal(
149156#[ test]
150157fn test_compute_fee_actual_u128_max_does_not_overflow ( ) {
151158 // Naive (a+b)/2 would overflow when a and b are near u128::MAX.
152- let proposals = vec ! [ GasPrice ( u128 :: MAX ) ; 10 ] ;
153- assert_eq ! ( compute_fee_actual( & proposals , 10 ) , Some ( GasPrice ( u128 :: MAX ) ) ) ;
159+ let window = window_from ( ( 0u64 .. 10 ) . map ( |h| ( h , Some ( GasPrice ( u128:: MAX ) ) ) ) ) ;
160+ assert_eq ! ( compute_fee_actual( & window , BlockNumber ( 10 ) ) , Some ( GasPrice ( u128 :: MAX ) ) ) ;
154161}
155162
156163#[ test]
@@ -183,7 +190,62 @@ fn test_compute_fee_target_monotonic_in_strk_price() {
183190#[ test]
184191fn test_compute_fee_actual_lone_adversary_cannot_skew_median ( ) {
185192 // With 9 honest values and 1 outlier, median resists the adversary.
186- let mut window = vec ! [ GasPrice ( 1_000_000 ) ; 9 ] ;
187- window. push ( GasPrice ( u128:: MAX / 2 ) ) ;
188- assert_eq ! ( compute_fee_actual( & window, 10 ) , Some ( GasPrice ( 1_000_000 ) ) ) ;
193+ let window = window_from (
194+ ( 0u64 ..9 )
195+ . map ( |h| ( h, Some ( GasPrice ( 1_000_000 ) ) ) )
196+ . chain ( std:: iter:: once ( ( 9u64 , Some ( GasPrice ( u128:: MAX / 2 ) ) ) ) ) ,
197+ ) ;
198+ assert_eq ! ( compute_fee_actual( & window, BlockNumber ( 10 ) ) , Some ( GasPrice ( 1_000_000 ) ) ) ;
199+ }
200+
201+ /// The validator's accept predicate. Must stay in sync with
202+ /// `validate_proposal::is_proposal_init_valid` SNIP-35 bounds check.
203+ fn validator_accepts ( fee_actual : GasPrice , fee_proposal : GasPrice , margin_ppt : u128 ) -> bool {
204+ let lower = fee_actual. 0 . saturating_mul ( PPT_DENOMINATOR ) / ( PPT_DENOMINATOR + margin_ppt) ;
205+ let upper = fee_actual. 0 . saturating_mul ( PPT_DENOMINATOR + margin_ppt) / PPT_DENOMINATOR ;
206+ fee_proposal. 0 >= lower && fee_proposal. 0 <= upper
207+ }
208+
209+ #[ test]
210+ fn test_malicious_high_fee_proposal_rejected ( ) {
211+ // Upper bound for fee_actual=1_000_000 with margin=2ppt is 1_002_000.
212+ let fee_actual = GasPrice ( 1_000_000 ) ;
213+ assert ! ( validator_accepts( fee_actual, GasPrice ( 1_002_000 ) , 2 ) ) ;
214+ for proposal in [ 1_002_001u128 , 1_003_000 , 2_000_000 , u128:: MAX ] {
215+ assert ! ( !validator_accepts( fee_actual, GasPrice ( proposal) , 2 ) , "accepted {proposal}" ) ;
216+ }
217+ }
218+
219+ #[ test]
220+ fn test_malicious_low_fee_proposal_rejected ( ) {
221+ // Lower bound for fee_actual=1_000_000 with margin=2ppt is 998_003.
222+ let fee_actual = GasPrice ( 1_000_000 ) ;
223+ assert ! ( validator_accepts( fee_actual, GasPrice ( 998_003 ) , 2 ) ) ;
224+ for proposal in [ 998_002u128 , 500_000 , 1 , 0 ] {
225+ assert ! ( !validator_accepts( fee_actual, GasPrice ( proposal) , 2 ) , "accepted {proposal}" ) ;
226+ }
227+ }
228+
229+ #[ test]
230+ fn test_honest_proposer_always_passes_validation_fuzzed ( ) {
231+ // Consensus safety: whatever compute_fee_proposal produces, the validator accepts.
232+ let mut rng = ChaCha8Rng :: seed_from_u64 ( 0xDEADBEEF ) ;
233+ for _ in 0 ..10_000 {
234+ let fee_actual_value = rng. gen_range ( 1u128 ..1_000_000_000_000_000_000 ) ;
235+ let strk_usd_rate = rng. gen_range ( 1u128 ..2 * 10u128 . pow ( 18 ) ) ;
236+ let fee_actual = GasPrice ( fee_actual_value) ;
237+ let target = compute_fee_target (
238+ TARGET_ATTO_USD_PER_L2_GAS ,
239+ strk_usd_rate,
240+ ORACLE_L2_GAS_FLOOR_MIN_FRI ,
241+ ORACLE_L2_GAS_FLOOR_MAX_FRI ,
242+ ) ;
243+ let oracle_result = if rng. gen_bool ( 0.1 ) { None } else { Some ( target) } ;
244+ let proposal = compute_fee_proposal ( oracle_result, fee_actual, FEE_PROPOSAL_MARGIN_PPT ) ;
245+ assert ! (
246+ validator_accepts( fee_actual, proposal, FEE_PROPOSAL_MARGIN_PPT ) ,
247+ "fee_actual={fee_actual_value} rate={strk_usd_rate} proposal={}" ,
248+ proposal. 0
249+ ) ;
250+ }
189251}
0 commit comments