17
17
package core
18
18
19
19
import (
20
+ "bytes"
20
21
"fmt"
21
22
"math"
22
23
"math/big"
@@ -79,12 +80,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
79
80
// Bump the required gas by the amount of transactional data
80
81
if dataLen > 0 {
81
82
// 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
+
88
86
// Make sure we don't exceed uint64 for all data combinations
89
87
nonZeroGas := params .TxDataNonZeroGasFrontier
90
88
if isEIP2028 {
@@ -95,7 +93,6 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
95
93
}
96
94
gas += nz * nonZeroGas
97
95
98
- z := dataLen - nz
99
96
if (math .MaxUint64 - gas )/ params .TxDataZeroGas < z {
100
97
return 0 , ErrGasUintOverflow
101
98
}
@@ -119,6 +116,21 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
119
116
return gas , nil
120
117
}
121
118
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
+
122
134
// toWordSize returns the ceiled word size required for init code payment calculation.
123
135
func toWordSize (size uint64 ) uint64 {
124
136
if size > math .MaxUint64 - 31 {
@@ -414,6 +426,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
414
426
sender = vm .AccountRef (msg .From )
415
427
rules = st .evm .ChainConfig ().Rules (st .evm .Context .BlockNumber , st .evm .Context .Random != nil , st .evm .Context .Time )
416
428
contractCreation = msg .To == nil
429
+ floorDataGas uint64
417
430
)
418
431
419
432
// Check clauses 4-5, subtract intrinsic gas if everything is correct
@@ -424,6 +437,16 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
424
437
if st .gasRemaining < gas {
425
438
return nil , fmt .Errorf ("%w: have %d, want %d" , ErrIntrinsicGas , st .gasRemaining , gas )
426
439
}
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
+ }
427
450
if t := st .evm .Config .Tracer ; t != nil && t .OnGasChange != nil {
428
451
t .OnGasChange (st .gasRemaining , st .gasRemaining - gas , tracing .GasChangeTxIntrinsicGas )
429
452
}
@@ -487,14 +510,21 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
487
510
ret , st .gasRemaining , vmerr = st .evm .Call (sender , st .to (), msg .Data , st .gasRemaining , value )
488
511
}
489
512
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
+ }
497
525
}
526
+ st .returnGas ()
527
+
498
528
effectiveTip := msg .GasPrice
499
529
if rules .IsLondon {
500
530
effectiveTip = new (big.Int ).Sub (msg .GasFeeCap , st .evm .Context .BaseFee )
@@ -585,20 +615,28 @@ func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization)
585
615
return nil
586
616
}
587
617
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
+ }
591
628
if refund > st .state .GetRefund () {
592
629
refund = st .state .GetRefund ()
593
630
}
594
-
595
631
if st .evm .Config .Tracer != nil && st .evm .Config .Tracer .OnGasChange != nil && refund > 0 {
596
632
st .evm .Config .Tracer .OnGasChange (st .gasRemaining , st .gasRemaining + refund , tracing .GasChangeTxRefunds )
597
633
}
634
+ return refund
635
+ }
598
636
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 () {
602
640
remaining := uint256 .NewInt (st .gasRemaining )
603
641
remaining .Mul (remaining , uint256 .MustFromBig (st .msg .GasPrice ))
604
642
st .state .AddBalance (st .msg .From , remaining , tracing .BalanceIncreaseGasReturn )
@@ -610,8 +648,6 @@ func (st *stateTransition) refundGas(refundQuotient uint64) uint64 {
610
648
// Also return remaining gas to the block gas counter so it is
611
649
// available for the next transaction.
612
650
st .gp .AddGas (st .gasRemaining )
613
-
614
- return refund
615
651
}
616
652
617
653
// gasUsed returns the amount of gas used up by the state transition.
0 commit comments