Skip to content

fix: block header hash not available in staking module #236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions x/vm/keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

const DefaultHeaderHashNum = int64(10000)

// BeginBlock emits a base fee event which will be adjusted to the evm decimals
func (k *Keeper) BeginBlock(ctx sdk.Context) error {
logger := ctx.Logger().With("begin_block", "evm")
Expand All @@ -31,6 +33,13 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error {
),
})
}

// store block header hash and rotate the old ones.
k.SetHeaderHash(ctx)
if i := ctx.BlockHeight() - DefaultHeaderHashNum; i > 0 {
k.DeleteHeaderHash(ctx, i)
}

return nil
}

Expand Down
18 changes: 18 additions & 0 deletions x/vm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,21 @@ func (k Keeper) AddTransientGasUsed(ctx sdk.Context, gasUsed uint64) (uint64, er
k.SetTransientGasUsed(ctx, result)
return result, nil
}

// SetHeaderHash stores the hash of the current block header in the store.
func (k Keeper) SetHeaderHash(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
store.Set(types.GetHeaderHashKey(ctx.BlockHeight()), ctx.HeaderHash())
}

// GetHeaderHash retrieves the hash of a block header from the store by height.
func (k Keeper) GetHeaderHash(ctx sdk.Context, height int64) []byte {
store := ctx.KVStore(k.storeKey)
return store.Get(types.GetHeaderHashKey(height))
}

// DeleteHeaderHash removes the hash of a block header from the store by height
func (k Keeper) DeleteHeaderHash(ctx sdk.Context, height int64) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetHeaderHashKey(height))
}
44 changes: 1 addition & 43 deletions x/vm/keeper/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"

cmttypes "github.com/cometbft/cometbft/types"

cosmosevmtypes "github.com/cosmos/evm/types"
"github.com/cosmos/evm/utils"
"github.com/cosmos/evm/x/vm/statedb"
Expand Down Expand Up @@ -86,47 +84,7 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc {
return common.Hash{}
}

switch {
case ctx.BlockHeight() == h:
// Case 1: The requested height matches the one from the context so we can retrieve the header
// hash directly from the context.
// Note: The headerHash is only set at begin block, it will be nil in case of a query context
headerHash := ctx.HeaderHash()
if len(headerHash) != 0 {
return common.BytesToHash(headerHash)
}

// only recompute the hash if not set (eg: checkTxState)
contextBlockHeader := ctx.BlockHeader()
header, err := cmttypes.HeaderFromProto(&contextBlockHeader)
if err != nil {
k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err)
return common.Hash{}
}

headerHash = header.Hash()
return common.BytesToHash(headerHash)

case ctx.BlockHeight() > h:
// Case 2: if the chain is not the current height we need to retrieve the hash from the store for the
// current chain epoch. This only applies if the current height is greater than the requested height.
histInfo, err := k.stakingKeeper.GetHistoricalInfo(ctx, h)
if err != nil {
k.Logger(ctx).Debug("error while getting historical info", "height", h, "error", err.Error())
return common.Hash{}
}

header, err := cmttypes.HeaderFromProto(&histInfo.Header)
if err != nil {
k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err)
return common.Hash{}
}

return common.BytesToHash(header.Hash())
default:
// Case 3: heights greater than the current one returns an empty hash.
return common.Hash{}
}
return common.BytesToHash(k.GetHeaderHash(ctx, h))
}
}

Expand Down
19 changes: 15 additions & 4 deletions x/vm/types/key.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types

import (
"encoding/binary"

"github.com/ethereum/go-ethereum/common"
)

Expand All @@ -27,6 +29,7 @@ const (
prefixStorage
prefixParams
prefixCodeHash
prefixHeaderHash
)

// prefix bytes for the EVM transient store
Expand All @@ -39,10 +42,11 @@ const (

// KVStore key prefixes
var (
KeyPrefixCode = []byte{prefixCode}
KeyPrefixStorage = []byte{prefixStorage}
KeyPrefixParams = []byte{prefixParams}
KeyPrefixCodeHash = []byte{prefixCodeHash}
KeyPrefixCode = []byte{prefixCode}
KeyPrefixStorage = []byte{prefixStorage}
KeyPrefixParams = []byte{prefixParams}
KeyPrefixCodeHash = []byte{prefixCodeHash}
KeyPrefixHeaderHash = []byte{prefixHeaderHash}
)

// Transient Store key prefixes
Expand All @@ -62,3 +66,10 @@ func AddressStoragePrefix(address common.Address) []byte {
func StateKey(address common.Address, key []byte) []byte {
return append(AddressStoragePrefix(address), key...)
}

func GetHeaderHashKey(height int64) []byte {
var key [1 + 8]byte
key[0] = prefixHeaderHash
binary.BigEndian.PutUint64(key[1:], uint64(height))
return key[:]
}