@@ -384,74 +384,82 @@ fn check_gas_limit(gas_limit: u64, parent_gas_limit: u64) -> bool {
384384
385385/// Calculates the base fee per blob gas for the current block based on
386386/// it's parent excess blob gas and the update fraction, which depends on the fork.
387- pub fn calculate_base_fee_per_blob_gas ( parent_excess_blob_gas : u64 , update_fraction : u64 ) -> u64 {
387+ pub fn calculate_base_fee_per_blob_gas ( parent_excess_blob_gas : u64 , update_fraction : u64 ) -> U256 {
388388 if update_fraction == 0 {
389- return 0 ;
389+ return U256 :: zero ( ) ;
390390 }
391391 fake_exponential (
392- MIN_BASE_FEE_PER_BLOB_GAS ,
393- parent_excess_blob_gas,
392+ U256 :: from ( MIN_BASE_FEE_PER_BLOB_GAS ) ,
393+ U256 :: from ( parent_excess_blob_gas) ,
394394 update_fraction,
395395 )
396+ . unwrap_or_default ( )
396397}
397398
398- // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844)
399- pub fn fake_exponential ( factor : u64 , numerator : u64 , denominator : u64 ) -> u64 {
400- let mut i = 1 ;
401- let mut output = U256 :: zero ( ) ;
402- let mut numerator_accum = U256 :: from ( factor) * denominator;
403- while !numerator_accum. is_zero ( ) {
404- output += numerator_accum;
405- numerator_accum = numerator_accum * numerator / ( denominator * i) ;
406- i += 1 ;
407- }
408- if ( output / denominator) > U256 :: from ( u64:: MAX ) {
409- u64:: MAX
410- } else {
411- ( output / denominator) . as_u64 ( )
412- }
413- }
414-
415- #[ derive( Debug , thiserror:: Error ) ]
416- pub enum FakeExponentialError {
417- #[ error( "Denominator cannot be zero." ) ]
418- DenominatorIsZero ,
419- #[ error( "Checked div failed is None." ) ]
420- CheckedDiv ,
421- #[ error( "Checked mul failed is None." ) ]
422- CheckedMul ,
423- }
424-
425- pub fn fake_exponential_checked (
426- factor : u64 ,
427- numerator : u64 ,
399+ /// Approximates factor * e ** (numerator / denominator) using Taylor expansion
400+ /// https://eips.ethereum.org/EIPS/eip-4844#helpers
401+ /// 400_000_000 numerator is the limit for this operation to work with U256,
402+ /// it will overflow with a larger numerator
403+ pub fn fake_exponential (
404+ factor : U256 ,
405+ numerator : U256 ,
428406 denominator : u64 ,
429- ) -> Result < u64 , FakeExponentialError > {
430- let mut i = 1_u64 ;
431- let mut output = 0_u64 ;
432- let mut numerator_accum = factor * denominator;
407+ ) -> Result < U256 , FakeExponentialError > {
433408 if denominator == 0 {
434409 return Err ( FakeExponentialError :: DenominatorIsZero ) ;
435410 }
436411
437- while numerator_accum > 0 {
438- output = output. saturating_add ( numerator_accum) ;
412+ if numerator. is_zero ( ) {
413+ return Ok ( factor) ;
414+ }
439415
440- let denominator_i = denominator
441- . checked_mul ( i)
442- . ok_or ( FakeExponentialError :: CheckedMul ) ?;
416+ let mut output: U256 = U256 :: zero ( ) ;
417+ let denominator_u256: U256 = denominator. into ( ) ;
443418
444- if denominator_i == 0 {
445- return Err ( FakeExponentialError :: DenominatorIsZero ) ;
419+ // Initial multiplication: factor * denominator
420+ let mut numerator_accum = factor
421+ . checked_mul ( denominator_u256)
422+ . ok_or ( FakeExponentialError :: CheckedMul ) ?;
423+
424+ let mut denominator_by_i = denominator_u256;
425+
426+ #[ expect(
427+ clippy:: arithmetic_side_effects,
428+ reason = "division can't overflow since denominator is not 0"
429+ ) ]
430+ {
431+ while !numerator_accum. is_zero ( ) {
432+ // Safe addition to output
433+ output = output
434+ . checked_add ( numerator_accum)
435+ . ok_or ( FakeExponentialError :: CheckedAdd ) ?;
436+
437+ // Safe multiplication and division within loop
438+ numerator_accum = numerator_accum
439+ . checked_mul ( numerator)
440+ . ok_or ( FakeExponentialError :: CheckedMul ) ?
441+ / denominator_by_i;
442+
443+ // denominator comes from a u64 value, will never overflow before other variables.
444+ denominator_by_i += denominator_u256;
446445 }
447446
448- numerator_accum = numerator_accum * numerator / denominator_i;
449- i += 1 ;
447+ output
448+ . checked_div ( denominator. into ( ) )
449+ . ok_or ( FakeExponentialError :: CheckedDiv )
450450 }
451+ }
451452
452- output
453- . checked_div ( denominator)
454- . ok_or ( FakeExponentialError :: CheckedDiv )
453+ #[ derive( Debug , thiserror:: Error , Serialize , Clone , PartialEq , Deserialize , Eq ) ]
454+ pub enum FakeExponentialError {
455+ #[ error( "FakeExponentialError: Denominator cannot be zero." ) ]
456+ DenominatorIsZero ,
457+ #[ error( "FakeExponentialError: Checked div failed is None." ) ]
458+ CheckedDiv ,
459+ #[ error( "FakeExponentialError: Checked mul failed is None." ) ]
460+ CheckedMul ,
461+ #[ error( "FakeExponentialError: Checked add failed is None." ) ]
462+ CheckedAdd ,
455463}
456464
457465// Calculates the base fee for the current block based on its gas_limit and parent's gas and fee
@@ -748,8 +756,8 @@ pub fn calc_excess_blob_gas(parent: &BlockHeader, schedule: ForkBlobSchedule, fo
748756 }
749757
750758 if fork >= Fork :: Osaka
751- && BLOB_BASE_COST * parent_base_fee_per_gas
752- > ( GAS_PER_BLOB as u64 )
759+ && U256 :: from ( BLOB_BASE_COST * parent_base_fee_per_gas)
760+ > ( U256 :: from ( GAS_PER_BLOB ) )
753761 * calculate_base_fee_per_blob_gas (
754762 parent_excess_blob_gas,
755763 schedule. base_fee_update_fraction ,
@@ -767,7 +775,7 @@ pub fn calc_excess_blob_gas(parent: &BlockHeader, schedule: ForkBlobSchedule, fo
767775mod test {
768776 use super :: * ;
769777 use crate :: constants:: EMPTY_KECCACK_HASH ;
770- use crate :: types:: ELASTICITY_MULTIPLIER ;
778+ use crate :: types:: { BLOB_BASE_FEE_UPDATE_FRACTION , ELASTICITY_MULTIPLIER } ;
771779 use ethereum_types:: H160 ;
772780 use hex_literal:: hex;
773781 use std:: str:: FromStr ;
@@ -991,6 +999,18 @@ mod test {
991999 #[ test]
9921000 fn test_fake_exponential_overflow ( ) {
9931001 // With u64 this overflows
994- fake_exponential ( 57532635 , 3145728 , 3338477 ) ;
1002+ assert ! ( fake_exponential( U256 :: from( 57532635 ) , U256 :: from( 3145728 ) , 3338477 ) . is_ok( ) ) ;
1003+ }
1004+
1005+ #[ test]
1006+ fn test_fake_exponential_bounds_overflow ( ) {
1007+ // Making sure the limit we state in the documentation of 400_000_000 works
1008+ let thing = fake_exponential (
1009+ MIN_BASE_FEE_PER_BLOB_GAS . into ( ) ,
1010+ 400_000_000 . into ( ) ,
1011+ BLOB_BASE_FEE_UPDATE_FRACTION ,
1012+ ) ;
1013+ // With u64 this overflows
1014+ assert ! ( thing. is_ok( ) ) ;
9951015 }
9961016}
0 commit comments