1717package core
1818
1919import (
20+ "bytes"
2021 "fmt"
2122 "math"
2223 "math/big"
@@ -81,12 +82,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
8182 // Bump the required gas by the amount of transactional data
8283 if dataLen > 0 {
8384 // Zero and non-zero bytes are priced differently
84- var nz uint64
85- for _ , byt := range data {
86- if byt != 0 {
87- nz ++
88- }
89- }
85+ z := uint64 (bytes .Count (data , []byte {0 }))
86+ nz := dataLen - z
87+
9088 // Make sure we don't exceed uint64 for all data combinations
9189 nonZeroGas := params .TxDataNonZeroGasFrontier
9290 if isEIP2028 {
@@ -97,7 +95,6 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
9795 }
9896 gas += nz * nonZeroGas
9997
100- z := dataLen - nz
10198 if (math .MaxUint64 - gas )/ params .TxDataZeroGas < z {
10299 return 0 , ErrGasUintOverflow
103100 }
@@ -121,6 +118,21 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
121118 return gas , nil
122119}
123120
121+ // FloorDataGas computes the minimum gas required for a transaction based on its data tokens (EIP-7623).
122+ func FloorDataGas (data []byte ) (uint64 , error ) {
123+ var (
124+ z = uint64 (bytes .Count (data , []byte {0 }))
125+ nz = uint64 (len (data )) - z
126+ tokens = nz * params .TxTokenPerNonZeroByte + z
127+ )
128+ // Check for overflow
129+ if (math .MaxUint64 - params .TxGas )/ params .TxCostFloorPerToken < tokens {
130+ return 0 , ErrGasUintOverflow
131+ }
132+ // Minimum gas required for a transaction based on its data tokens (EIP-7623).
133+ return params .TxGas + tokens * params .TxCostFloorPerToken , nil
134+ }
135+
124136// toWordSize returns the ceiled word size required for init code payment calculation.
125137func toWordSize (size uint64 ) uint64 {
126138 if size > math .MaxUint64 - 31 {
@@ -454,6 +466,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
454466 sender = vm .AccountRef (msg .From )
455467 rules = st .evm .ChainConfig ().Rules (st .evm .Context .BlockNumber , st .evm .Context .Random != nil , st .evm .Context .Time )
456468 contractCreation = msg .To == nil
469+ floorDataGas uint64
457470 )
458471
459472 // Check clauses 4-5, subtract intrinsic gas if everything is correct
@@ -464,6 +477,16 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
464477 if st .gasRemaining < gas {
465478 return nil , fmt .Errorf ("%w: have %d, want %d" , ErrIntrinsicGas , st .gasRemaining , gas )
466479 }
480+ // Gas limit suffices for the floor data cost (EIP-7623)
481+ if rules .IsPrague {
482+ floorDataGas , err = FloorDataGas (msg .Data )
483+ if err != nil {
484+ return nil , err
485+ }
486+ if msg .GasLimit < floorDataGas {
487+ return nil , fmt .Errorf ("%w: have %d, want %d" , ErrFloorDataGas , msg .GasLimit , floorDataGas )
488+ }
489+ }
467490 if t := st .evm .Config .Tracer ; t != nil && t .OnGasChange != nil {
468491 t .OnGasChange (st .gasRemaining , st .gasRemaining - gas , tracing .GasChangeTxIntrinsicGas )
469492 }
@@ -527,14 +550,21 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
527550 ret , st .gasRemaining , vmerr = st .evm .Call (sender , st .to (), msg .Data , st .gasRemaining , value )
528551 }
529552
530- var gasRefund uint64
531- if ! rules .IsLondon {
532- // Before EIP-3529: refunds were capped to gasUsed / 2
533- gasRefund = st .refundGas (params .RefundQuotient )
534- } else {
535- // After EIP-3529: refunds are capped to gasUsed / 5
536- gasRefund = st .refundGas (params .RefundQuotientEIP3529 )
553+ // Compute refund counter, capped to a refund quotient.
554+ gasRefund := st .calcRefund ()
555+ st .gasRemaining += gasRefund
556+ if rules .IsPrague {
557+ // After EIP-7623: Data-heavy transactions pay the floor gas.
558+ if st .gasUsed () < floorDataGas {
559+ prev := st .gasRemaining
560+ st .gasRemaining = st .initialGas - floorDataGas
561+ if t := st .evm .Config .Tracer ; t != nil && t .OnGasChange != nil {
562+ t .OnGasChange (prev , st .gasRemaining , tracing .GasChangeTxDataFloor )
563+ }
564+ }
537565 }
566+ st .returnGas ()
567+
538568 effectiveTip := msg .GasPrice
539569 if rules .IsLondon {
540570 effectiveTip = new (big.Int ).Sub (msg .GasFeeCap , st .evm .Context .BaseFee )
@@ -625,20 +655,28 @@ func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization)
625655 return nil
626656}
627657
628- func (st * stateTransition ) refundGas (refundQuotient uint64 ) uint64 {
629- // Apply refund counter, capped to a refund quotient
630- refund := st .gasUsed () / refundQuotient
658+ // calcRefund computes refund counter, capped to a refund quotient.
659+ func (st * stateTransition ) calcRefund () uint64 {
660+ var refund uint64
661+ if ! st .evm .ChainConfig ().IsLondon (st .evm .Context .BlockNumber ) {
662+ // Before EIP-3529: refunds were capped to gasUsed / 2
663+ refund = st .gasUsed () / params .RefundQuotient
664+ } else {
665+ // After EIP-3529: refunds are capped to gasUsed / 5
666+ refund = st .gasUsed () / params .RefundQuotientEIP3529
667+ }
631668 if refund > st .state .GetRefund () {
632669 refund = st .state .GetRefund ()
633670 }
634-
635671 if st .evm .Config .Tracer != nil && st .evm .Config .Tracer .OnGasChange != nil && refund > 0 {
636672 st .evm .Config .Tracer .OnGasChange (st .gasRemaining , st .gasRemaining + refund , tracing .GasChangeTxRefunds )
637673 }
674+ return refund
675+ }
638676
639- st . gasRemaining += refund
640-
641- // Return ETH for remaining gas, exchanged at the original rate.
677+ // returnGas returns ETH for remaining gas,
678+ // exchanged at the original rate.
679+ func ( st * stateTransition ) returnGas () {
642680 remaining := uint256 .NewInt (st .gasRemaining )
643681 remaining .Mul (remaining , uint256 .MustFromBig (st .msg .GasPrice ))
644682 st .state .AddBalance (st .msg .From , remaining , tracing .BalanceIncreaseGasReturn )
@@ -650,8 +688,6 @@ func (st *stateTransition) refundGas(refundQuotient uint64) uint64 {
650688 // Also return remaining gas to the block gas counter so it is
651689 // available for the next transaction.
652690 st .gp .AddGas (st .gasRemaining )
653-
654- return refund
655691}
656692
657693// gasUsed returns the amount of gas used up by the state transition.
0 commit comments