Skip to content

Commit d915d3b

Browse files
committed
Merge remote-tracking branch 'origin/develop' into jt/export-headers-toolkit
2 parents 6ed9c95 + cf3d22e commit d915d3b

27 files changed

+409
-52
lines changed

cmd/evm/internal/t8ntool/execution.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
152152
if chainConfig.CurieBlock != nil && chainConfig.CurieBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
153153
misc.ApplyCurieHardFork(statedb)
154154
}
155+
// Apply EIP-2935
156+
if pre.Env.BlockHashes != nil && chainConfig.IsFeynman(pre.Env.Timestamp) {
157+
var (
158+
prevNumber = pre.Env.Number - 1
159+
prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)]
160+
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
161+
)
162+
core.ProcessParentBlockHash(prevHash, evm, statedb)
163+
}
155164

156165
for i, tx := range txs {
157166
msg, err := tx.AsMessage(signer, pre.Env.BaseFee)

cmd/evm/internal/t8ntool/transaction.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,29 @@ func Transaction(ctx *cli.Context) error {
140140
r.Address = sender
141141
}
142142
// Check intrinsic gas
143-
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil,
144-
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int))); err != nil {
143+
rules := chainConfig.Rules(common.Big0, 0)
144+
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
145+
if err != nil {
145146
r.Error = err
146147
results = append(results, r)
147148
continue
148-
} else {
149-
r.IntrinsicGas = gas
150-
if tx.Gas() < gas {
151-
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas)
149+
}
150+
r.IntrinsicGas = gas
151+
if tx.Gas() < gas {
152+
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas)
153+
results = append(results, r)
154+
continue
155+
}
156+
// For Feynman txs, validate the floor data gas.
157+
if rules.IsFeynman {
158+
floorDataGas, err := core.FloorDataGas(tx.Data())
159+
if err != nil {
160+
r.Error = err
161+
results = append(results, r)
162+
continue
163+
}
164+
if tx.Gas() < floorDataGas {
165+
r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrFloorDataGas, tx.Gas(), floorDataGas)
152166
results = append(results, r)
153167
continue
154168
}

core/error.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ var (
8181
// than required to start the invocation.
8282
ErrIntrinsicGas = errors.New("intrinsic gas too low")
8383

84+
// ErrFloorDataGas is returned if the transaction is specified to use less gas
85+
// than required for the data floor cost.
86+
ErrFloorDataGas = errors.New("insufficient gas for floor data gas cost")
87+
8488
// ErrTxTypeNotSupported is returned if a transaction is not supported in the
8589
// current network configuration.
8690
ErrTxTypeNotSupported = types.ErrTxTypeNotSupported

core/genesis.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,8 @@ func DeveloperGenesisBlock(period uint64, gasLimit uint64, faucet common.Address
500500
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
501501
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
502502
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
503+
// Pre-deploy EIP-2935 history contract.
504+
params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0},
503505
// LSH 250 due to finite field limitation
504506
faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 250), big.NewInt(9))},
505507
},

core/state/statedb.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ func (s *StateDB) WithWitness(witness *stateless.Witness) {
167167
s.witness = witness
168168
}
169169

170+
// Witness returns the current witness for the state database.
171+
func (s *StateDB) Witness() *stateless.Witness {
172+
return s.witness
173+
}
174+
170175
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
171176
// state trie concurrently while the state is mutated so that when we reach the
172177
// commit phase, most of the needed data is already hot.

core/state_processor.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
9494
blockContext := NewEVMBlockContext(header, p.bc, p.config, nil)
9595
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
9696
processorBlockTransactionGauge.Update(int64(block.Transactions().Len()))
97+
// Apply EIP-2935
98+
if p.config.IsFeynman(block.Time()) {
99+
ProcessParentBlockHash(block.ParentHash(), vmenv, statedb)
100+
}
97101
// Iterate over and process the individual transactions
98102
for i, tx := range block.Transactions() {
99103
msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number, header.Time), header.BaseFee)
@@ -199,3 +203,30 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
199203
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
200204
return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
201205
}
206+
207+
// ProcessParentBlockHash stores the parent block hash in the history storage contract
208+
// as per EIP-2935.
209+
func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM, statedb *state.StateDB) {
210+
msg := types.NewMessage(
211+
params.SystemAddress, // from
212+
&params.HistoryStorageAddress, // to
213+
0, // nonce
214+
common.Big0, // amount
215+
30_000_000, // gasLimit
216+
common.Big0, // gasPrice
217+
common.Big0, // gasFeeCap
218+
common.Big0, // gasTipCap
219+
prevHash.Bytes(), // data
220+
nil, // accessList
221+
false, // isFake
222+
nil, // setCodeAuthorizations
223+
)
224+
225+
evm.Reset(NewEVMTxContext(msg), statedb)
226+
statedb.AddAddressToAccessList(params.HistoryStorageAddress)
227+
_, _, err := evm.Call(vm.AccountRef(msg.From()), *msg.To(), msg.Data(), 30_000_000, common.Big0, nil)
228+
if err != nil {
229+
panic(err)
230+
}
231+
statedb.Finalise(true)
232+
}

core/state_processor_test.go

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package core
1818

1919
import (
2020
"crypto/ecdsa"
21+
"encoding/binary"
2122
"math/big"
2223
"testing"
2324

@@ -31,9 +32,11 @@ import (
3132
"github.com/scroll-tech/go-ethereum/consensus/ethash"
3233
"github.com/scroll-tech/go-ethereum/consensus/misc"
3334
"github.com/scroll-tech/go-ethereum/core/rawdb"
35+
"github.com/scroll-tech/go-ethereum/core/state"
3436
"github.com/scroll-tech/go-ethereum/core/types"
3537
"github.com/scroll-tech/go-ethereum/core/vm"
3638
"github.com/scroll-tech/go-ethereum/crypto"
39+
"github.com/scroll-tech/go-ethereum/ethdb/memorydb"
3740
"github.com/scroll-tech/go-ethereum/params"
3841
"github.com/scroll-tech/go-ethereum/trie"
3942
)
@@ -64,6 +67,7 @@ func TestStateProcessorErrors(t *testing.T) {
6467
DarwinV2Time: new(uint64),
6568
EuclidTime: new(uint64),
6669
EuclidV2Time: new(uint64),
70+
FeynmanTime: new(uint64),
6771
Ethash: new(params.EthashConfig),
6872
}
6973
signer = types.LatestSigner(config)
@@ -387,9 +391,9 @@ func TestStateProcessorErrors(t *testing.T) {
387391
}{
388392
{ // ErrMaxInitCodeSizeExceeded
389393
txs: []*types.Transaction{
390-
mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), tooBigInitCode[:]),
394+
mkDynamicCreationTx(0, 520000, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), tooBigInitCode[:]),
391395
},
392-
want: "could not apply tx 0 [0x7b33776d375660694a23ef992c090265682f3687607e0099b14503fdb65d73e3]: max initcode size exceeded: code size 49153 limit 49152",
396+
want: "could not apply tx 0 [0xe0d03426cecc04467410064cb4de02012fc069d2462282735d7dfcb9dea9f63b]: max initcode size exceeded: code size 49153 limit 49152",
393397
},
394398
{ // ErrIntrinsicGas: Not enough gas to cover init code
395399
txs: []*types.Transaction{
@@ -453,3 +457,68 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
453457
// Assemble and return the final block for sealing
454458
return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil))
455459
}
460+
461+
func TestProcessParentBlockHash(t *testing.T) {
462+
var (
463+
chainConfig = &params.ChainConfig{
464+
ChainID: big.NewInt(1),
465+
HomesteadBlock: big.NewInt(0),
466+
EIP150Block: big.NewInt(0),
467+
EIP155Block: big.NewInt(0),
468+
EIP158Block: big.NewInt(0),
469+
ByzantiumBlock: big.NewInt(0),
470+
ConstantinopleBlock: big.NewInt(0),
471+
PetersburgBlock: big.NewInt(0),
472+
IstanbulBlock: big.NewInt(0),
473+
MuirGlacierBlock: big.NewInt(0),
474+
BerlinBlock: big.NewInt(0),
475+
LondonBlock: big.NewInt(0),
476+
ShanghaiBlock: big.NewInt(0),
477+
BernoulliBlock: big.NewInt(0),
478+
CurieBlock: big.NewInt(0),
479+
DarwinTime: new(uint64),
480+
DarwinV2Time: new(uint64),
481+
EuclidTime: new(uint64),
482+
EuclidV2Time: new(uint64),
483+
FeynmanTime: new(uint64),
484+
Ethash: new(params.EthashConfig),
485+
}
486+
hashA = common.Hash{0x01}
487+
hashB = common.Hash{0x02}
488+
header = &types.Header{ParentHash: hashA, Number: big.NewInt(2), Difficulty: big.NewInt(0)}
489+
parent = &types.Header{ParentHash: hashB, Number: big.NewInt(1), Difficulty: big.NewInt(0)}
490+
coinbase = common.Address{}
491+
)
492+
test := func(statedb *state.StateDB) {
493+
statedb.SetNonce(params.HistoryStorageAddress, 1)
494+
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
495+
statedb.IntermediateRoot(true)
496+
497+
vmContext := NewEVMBlockContext(header, nil, chainConfig, &coinbase)
498+
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
499+
ProcessParentBlockHash(header.ParentHash, evm, statedb)
500+
501+
vmContext = NewEVMBlockContext(parent, nil, chainConfig, &coinbase)
502+
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
503+
ProcessParentBlockHash(parent.ParentHash, evm, statedb)
504+
505+
// make sure that the state is correct
506+
if have := getParentBlockHash(statedb, 1); have != hashA {
507+
t.Errorf("want parent hash %v, have %v", hashA, have)
508+
}
509+
if have := getParentBlockHash(statedb, 0); have != hashB {
510+
t.Errorf("want parent hash %v, have %v", hashB, have)
511+
}
512+
}
513+
t.Run("MPT", func(t *testing.T) {
514+
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
515+
test(statedb)
516+
})
517+
}
518+
519+
func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash {
520+
ringIndex := number % params.HistoryServeWindow
521+
var key common.Hash
522+
binary.BigEndian.PutUint64(key[24:], ringIndex)
523+
return statedb.GetState(params.HistoryStorageAddress, key)
524+
}

core/state_transition.go

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package core
1818

1919
import (
20+
"bytes"
2021
"fmt"
2122
"math"
2223
"math/big"
@@ -141,12 +142,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
141142
// Bump the required gas by the amount of transactional data
142143
if dataLen > 0 {
143144
// Zero and non-zero bytes are priced differently
144-
var nz uint64
145-
for _, byt := range data {
146-
if byt != 0 {
147-
nz++
148-
}
149-
}
145+
z := uint64(bytes.Count(data, []byte{0}))
146+
nz := dataLen - z
147+
150148
// Make sure we don't exceed uint64 for all data combinations
151149
nonZeroGas := params.TxDataNonZeroGasFrontier
152150
if isEIP2028 {
@@ -157,7 +155,6 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
157155
}
158156
gas += nz * nonZeroGas
159157

160-
z := dataLen - nz
161158
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
162159
return 0, ErrGasUintOverflow
163160
}
@@ -181,6 +178,21 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
181178
return gas, nil
182179
}
183180

181+
// FloorDataGas computes the minimum gas required for a transaction based on its data tokens (EIP-7623).
182+
func FloorDataGas(data []byte) (uint64, error) {
183+
var (
184+
z = uint64(bytes.Count(data, []byte{0}))
185+
nz = uint64(len(data)) - z
186+
tokens = nz*params.TxTokenPerNonZeroByte + z
187+
)
188+
// Check for overflow
189+
if (math.MaxUint64-params.TxGas)/params.TxCostFloorPerToken < tokens {
190+
return 0, ErrGasUintOverflow
191+
}
192+
// Minimum gas required for a transaction based on its data tokens (EIP-7623).
193+
return params.TxGas + tokens*params.TxCostFloorPerToken, nil
194+
}
195+
184196
// toWordSize returns the ceiled word size required for init code payment calculation.
185197
func toWordSize(size uint64) uint64 {
186198
if size > math.MaxUint64-31 {
@@ -380,16 +392,30 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
380392
sender = vm.AccountRef(msg.From())
381393
rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Time.Uint64())
382394
contractCreation = msg.To() == nil
395+
floorDataGas uint64
383396
)
384397

385398
// Check clauses 4-5, subtract intrinsic gas if everything is correct
386399
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), st.msg.SetCodeAuthorizations(), contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
387400
if err != nil {
401+
// Note: The L1 message queue contract ensures that this cannot happen for L1 messages.
388402
return nil, err
389403
}
390404
if st.gas < gas {
405+
// Note: The L1 message queue contract ensures that this cannot happen for L1 messages.
391406
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas)
392407
}
408+
// Gas limit suffices for the floor data cost (EIP-7623)
409+
if rules.IsFeynman {
410+
floorDataGas, err = FloorDataGas(st.data)
411+
if err != nil {
412+
return nil, err
413+
}
414+
if st.gas < floorDataGas {
415+
// Note: The L1 message queue contract ensures that this cannot happen for L1 messages.
416+
return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, st.gas, floorDataGas)
417+
}
418+
}
393419
st.gas -= gas
394420

395421
// Check clause 6
@@ -455,13 +481,16 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
455481
}, nil
456482
}
457483

458-
if !rules.IsLondon {
459-
// Before EIP-3529: refunds were capped to gasUsed / 2
460-
st.refundGas(params.RefundQuotient)
461-
} else {
462-
// After EIP-3529: refunds are capped to gasUsed / 5
463-
st.refundGas(params.RefundQuotientEIP3529)
484+
// Compute refund counter, capped to a refund quotient.
485+
st.gas += st.calcRefund()
486+
if rules.IsFeynman {
487+
// After EIP-7623: Data-heavy transactions pay the floor gas.
488+
if st.gasUsed() < floorDataGas {
489+
st.gas = st.initialGas - floorDataGas
490+
}
464491
}
492+
st.returnGas()
493+
465494
effectiveTip := st.gasPrice
466495

467496
// only burn the base fee if the fee vault is not enabled
@@ -545,14 +574,25 @@ func (st *StateTransition) applyAuthorization(auth *types.SetCodeAuthorization)
545574
return types.AuthorizationResult{Authority: authority, PreCode: preCode, Success: true}
546575
}
547576

548-
func (st *StateTransition) refundGas(refundQuotient uint64) {
549-
// Apply refund counter, capped to a refund quotient
550-
refund := st.gasUsed() / refundQuotient
577+
// calcRefund computes refund counter, capped to a refund quotient.
578+
func (st *StateTransition) calcRefund() uint64 {
579+
var refund uint64
580+
if !st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
581+
// Before EIP-3529: refunds were capped to gasUsed / 2
582+
refund = st.gasUsed() / params.RefundQuotient
583+
} else {
584+
// After EIP-3529: refunds are capped to gasUsed / 5
585+
refund = st.gasUsed() / params.RefundQuotientEIP3529
586+
}
551587
if refund > st.state.GetRefund() {
552588
refund = st.state.GetRefund()
553589
}
554-
st.gas += refund
590+
return refund
591+
}
555592

593+
// returnGas returns ETH for remaining gas,
594+
// exchanged at the original rate.
595+
func (st *StateTransition) returnGas() {
556596
// Return ETH for remaining gas, exchanged at the original rate.
557597
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
558598
st.state.AddBalance(st.msg.From(), remaining)

0 commit comments

Comments
 (0)