1616package keeper
1717
1818import (
19+ "encoding/binary"
1920 "math/big"
2021
2122 "github.com/ethereum/go-ethereum/crypto"
@@ -31,15 +32,15 @@ import (
3132 "github.com/ethereum/go-ethereum/core/tracing"
3233 ethtypes "github.com/ethereum/go-ethereum/core/types"
3334 "github.com/ethereum/go-ethereum/core/vm"
34- "github.com/ethereum/go-ethereum/params"
35+ ethparams "github.com/ethereum/go-ethereum/params"
3536 ethermint "github.com/evmos/ethermint/types"
3637 "github.com/evmos/ethermint/x/evm/statedb"
3738 "github.com/evmos/ethermint/x/evm/types"
3839 "github.com/holiman/uint256"
3940)
4041
4142// CustomContractFn defines a custom precompiled contract generator with ctx, rules and returns a precompiled contract.
42- type CustomContractFn func (sdk.Context , params .Rules ) vm.PrecompiledContract
43+ type CustomContractFn func (sdk.Context , ethparams .Rules ) vm.PrecompiledContract
4344
4445// GasNoLimit is the value for keeper.queryMaxGasLimit in case there is no limit
4546const GasNoLimit = 0
@@ -216,7 +217,7 @@ func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg *core.Message, receipt *e
216217}
217218
218219// Tracer return a default vm.Tracer based on current keeper state
219- func (k Keeper ) Tracer (ctx sdk.Context , msg core.Message , ethCfg * params .ChainConfig ) * tracing.Hooks {
220+ func (k Keeper ) Tracer (ctx sdk.Context , msg core.Message , ethCfg * ethparams .ChainConfig ) * tracing.Hooks {
220221 return types .NewTracer (k .tracer , msg , ethCfg , ctx .BlockHeight (), uint64 (ctx .BlockTime ().Unix ())) //#nosec G115 -- int overflow is not a concern here
221222}
222223
@@ -278,7 +279,7 @@ func (k *Keeper) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string)
278279// - `nil`: london hardfork not enabled.
279280// - `0`: london hardfork enabled but feemarket is not enabled.
280281// - `n`: both london hardfork and feemarket are enabled.
281- func (k Keeper ) GetBaseFee (ctx sdk.Context , ethCfg * params .ChainConfig ) * big.Int {
282+ func (k Keeper ) GetBaseFee (ctx sdk.Context , ethCfg * ethparams .ChainConfig ) * big.Int {
282283 return k .getBaseFee (ctx , types .IsLondon (ethCfg , ctx .BlockHeight ()))
283284}
284285
@@ -322,18 +323,57 @@ func (k Keeper) AddTransientGasUsed(ctx sdk.Context, gasUsed uint64) (uint64, er
322323
323324// SetHeaderHash stores the hash of the current block header in the store.
324325func (k Keeper ) SetHeaderHash (ctx sdk.Context ) {
325- store := ctx .KVStore (k .storeKey )
326- height , err := ethermint .SafeUint64 (ctx .BlockHeight ())
327- if err != nil {
328- panic (err )
326+ acct := k .GetAccount (ctx , ethparams .HistoryStorageAddress )
327+ if acct != nil && acct .IsContract () {
328+ window := types .DefaultHistoryServeWindow
329+ params := k .GetParams (ctx )
330+ if params .HistoryServeWindow > 0 {
331+ window = params .HistoryServeWindow
332+ }
333+ // set current block hash in the contract storage, compatible with EIP-2935
334+ ringIndex := uint64 (ctx .BlockHeight ()) % window //nolint:gosec // G115 // won't exceed uint64
335+ var key common.Hash
336+ binary .BigEndian .PutUint64 (key [24 :], ringIndex )
337+ k .SetState (ctx , ethparams .HistoryStorageAddress , key , ctx .HeaderHash ())
338+ } else {
339+ // fallback old implementation
340+ store := ctx .KVStore (k .storeKey )
341+ height , err := ethermint .SafeUint64 (ctx .BlockHeight ())
342+ if err != nil {
343+ panic (err )
344+ }
345+ store .Set (types .GetHeaderHashKey (height ), ctx .HeaderHash ())
329346 }
330- store .Set (types .GetHeaderHashKey (height ), ctx .HeaderHash ())
331347}
332348
333- // GetHeaderHash retrieves the hash of a block header from the store by height.
334- func (k Keeper ) GetHeaderHash (ctx sdk.Context , height uint64 ) []byte {
349+ // GetHeaderHash sets block hash into EIP-2935 compatible storage contract.
350+ func (k Keeper ) GetHeaderHash (ctx sdk.Context , height uint64 ) common.Hash {
351+ // check if history contract has been deployed
352+ acct := k .GetAccount (ctx , ethparams .HistoryStorageAddress )
353+ if acct != nil && acct .IsContract () {
354+ window := types .DefaultHistoryServeWindow
355+ params := k .GetParams (ctx )
356+ if params .HistoryServeWindow > 0 {
357+ window = params .HistoryServeWindow
358+ }
359+
360+ ringIndex := height % window
361+ var key common.Hash
362+ binary .BigEndian .PutUint64 (key [24 :], ringIndex )
363+ hash := k .GetState (ctx , ethparams .HistoryStorageAddress , key )
364+
365+ if hash .Cmp (common.Hash {}) != 0 {
366+ return hash
367+ }
368+ }
369+ // fall back to old behavior for retro compatibility
370+ // TODO can be removed along with DeleteHeaderHash once HistoryStorage has been filled up in next protocol upgrade
335371 store := ctx .KVStore (k .storeKey )
336- return store .Get (types .GetHeaderHashKey (height ))
372+ hashByte := store .Get (types .GetHeaderHashKey (height ))
373+ if len (hashByte ) > 0 {
374+ return common .BytesToHash (hashByte )
375+ }
376+ return common.Hash {}
337377}
338378
339379// DeleteHeaderHash removes the hash of a block header from the store by height
0 commit comments