1717package core
1818
1919import (
20+ "bytes"
2021 "fmt"
2122 "math"
2223 "math/big"
@@ -79,12 +80,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
7980 // Bump the required gas by the amount of transactional data
8081 if dataLen > 0 {
8182 // Zero and non-zero bytes are priced differently
82- var nz uint64
83- for _ , byt := range data {
84- if byt != 0 {
85- nz ++
86- }
87- }
83+ z := uint64 (bytes .Count (data , []byte {0 }))
84+ nz := dataLen - z
85+
8886 // Make sure we don't exceed uint64 for all data combinations
8987 nonZeroGas := params .TxDataNonZeroGasFrontier
9088 if isEIP2028 {
@@ -95,7 +93,6 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
9593 }
9694 gas += nz * nonZeroGas
9795
98- z := dataLen - nz
9996 if (math .MaxUint64 - gas )/ params .TxDataZeroGas < z {
10097 return 0 , ErrGasUintOverflow
10198 }
@@ -119,6 +116,21 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
119116 return gas , nil
120117}
121118
119+ // FloorDataGas computes the minimum gas required for a transaction based on its data tokens (EIP-7623).
120+ func FloorDataGas (data []byte ) (uint64 , error ) {
121+ var (
122+ z = uint64 (bytes .Count (data , []byte {0 }))
123+ nz = uint64 (len (data )) - z
124+ tokens = nz * params .TxTokenPerNonZeroByte + z
125+ )
126+ // Check for overflow
127+ if (math .MaxUint64 - params .TxGas )/ params .TxCostFloorPerToken < tokens {
128+ return 0 , ErrGasUintOverflow
129+ }
130+ // Minimum gas required for a transaction based on its data tokens (EIP-7623).
131+ return params .TxGas + tokens * params .TxCostFloorPerToken , nil
132+ }
133+
122134// toWordSize returns the ceiled word size required for init code payment calculation.
123135func toWordSize (size uint64 ) uint64 {
124136 if size > math .MaxUint64 - 31 {
@@ -414,6 +426,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
414426 sender = vm .AccountRef (msg .From )
415427 rules = st .evm .ChainConfig ().Rules (st .evm .Context .BlockNumber , st .evm .Context .Random != nil , st .evm .Context .Time )
416428 contractCreation = msg .To == nil
429+ floorDataGas uint64
417430 )
418431
419432 // Check clauses 4-5, subtract intrinsic gas if everything is correct
@@ -424,6 +437,16 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
424437 if st .gasRemaining < gas {
425438 return nil , fmt .Errorf ("%w: have %d, want %d" , ErrIntrinsicGas , st .gasRemaining , gas )
426439 }
440+ // Gas limit suffices for the floor data cost (EIP-7623)
441+ if rules .IsPrague {
442+ floorDataGas , err = FloorDataGas (msg .Data )
443+ if err != nil {
444+ return nil , err
445+ }
446+ if msg .GasLimit < floorDataGas {
447+ return nil , fmt .Errorf ("%w: have %d, want %d" , ErrFloorDataGas , msg .GasLimit , floorDataGas )
448+ }
449+ }
427450 if t := st .evm .Config .Tracer ; t != nil && t .OnGasChange != nil {
428451 t .OnGasChange (st .gasRemaining , st .gasRemaining - gas , tracing .GasChangeTxIntrinsicGas )
429452 }
@@ -487,14 +510,21 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
487510 ret , st .gasRemaining , vmerr = st .evm .Call (sender , st .to (), msg .Data , st .gasRemaining , value )
488511 }
489512
490- var gasRefund uint64
491- if ! rules .IsLondon {
492- // Before EIP-3529: refunds were capped to gasUsed / 2
493- gasRefund = st .refundGas (params .RefundQuotient )
494- } else {
495- // After EIP-3529: refunds are capped to gasUsed / 5
496- gasRefund = st .refundGas (params .RefundQuotientEIP3529 )
513+ // Compute refund counter, capped to a refund quotient.
514+ gasRefund := st .calcRefund ()
515+ st .gasRemaining += gasRefund
516+ if rules .IsPrague {
517+ // After EIP-7623: Data-heavy transactions pay the floor gas.
518+ if st .gasUsed () < floorDataGas {
519+ prev := st .gasRemaining
520+ st .gasRemaining = st .initialGas - floorDataGas
521+ if t := st .evm .Config .Tracer ; t != nil && t .OnGasChange != nil {
522+ t .OnGasChange (prev , st .gasRemaining , tracing .GasChangeTxDataFloor )
523+ }
524+ }
497525 }
526+ st .returnGas ()
527+
498528 effectiveTip := msg .GasPrice
499529 if rules .IsLondon {
500530 effectiveTip = new (big.Int ).Sub (msg .GasFeeCap , st .evm .Context .BaseFee )
@@ -585,20 +615,28 @@ func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization)
585615 return nil
586616}
587617
588- func (st * stateTransition ) refundGas (refundQuotient uint64 ) uint64 {
589- // Apply refund counter, capped to a refund quotient
590- refund := st .gasUsed () / refundQuotient
618+ // calcRefund computes refund counter, capped to a refund quotient.
619+ func (st * stateTransition ) calcRefund () uint64 {
620+ var refund uint64
621+ if ! st .evm .ChainConfig ().IsLondon (st .evm .Context .BlockNumber ) {
622+ // Before EIP-3529: refunds were capped to gasUsed / 2
623+ refund = st .gasUsed () / params .RefundQuotient
624+ } else {
625+ // After EIP-3529: refunds are capped to gasUsed / 5
626+ refund = st .gasUsed () / params .RefundQuotientEIP3529
627+ }
591628 if refund > st .state .GetRefund () {
592629 refund = st .state .GetRefund ()
593630 }
594-
595631 if st .evm .Config .Tracer != nil && st .evm .Config .Tracer .OnGasChange != nil && refund > 0 {
596632 st .evm .Config .Tracer .OnGasChange (st .gasRemaining , st .gasRemaining + refund , tracing .GasChangeTxRefunds )
597633 }
634+ return refund
635+ }
598636
599- st . gasRemaining += refund
600-
601- // Return ETH for remaining gas, exchanged at the original rate.
637+ // returnGas returns ETH for remaining gas,
638+ // exchanged at the original rate.
639+ func ( st * stateTransition ) returnGas () {
602640 remaining := uint256 .NewInt (st .gasRemaining )
603641 remaining .Mul (remaining , uint256 .MustFromBig (st .msg .GasPrice ))
604642 st .state .AddBalance (st .msg .From , remaining , tracing .BalanceIncreaseGasReturn )
@@ -610,8 +648,6 @@ func (st *stateTransition) refundGas(refundQuotient uint64) uint64 {
610648 // Also return remaining gas to the block gas counter so it is
611649 // available for the next transaction.
612650 st .gp .AddGas (st .gasRemaining )
613-
614- return refund
615651}
616652
617653// gasUsed returns the amount of gas used up by the state transition.
0 commit comments