diff --git a/app/ante/interfaces.go b/app/ante/interfaces.go index a1323cfa55..dc32e239f3 100644 --- a/app/ante/interfaces.go +++ b/app/ante/interfaces.go @@ -47,8 +47,6 @@ type EVMKeeper interface { NewEVM(ctx sdk.Context, msg core.Message, cfg *statedb.EVMConfig, tracer vm.EVMLogger, stateDB vm.StateDB) evm.EVM DeductTxCostsFromUserBalance(ctx sdk.Context, fees sdk.Coins, from common.Address) error GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) *big.Int - ResetTransientGasUsed(ctx sdk.Context) - GetTxIndexTransient(ctx sdk.Context) uint64 GetParams(ctx sdk.Context) evmtypes.Params } diff --git a/app/ante/setup.go b/app/ante/setup.go index 60ccc02c91..137acea4fe 100644 --- a/app/ante/setup.go +++ b/app/ante/setup.go @@ -17,7 +17,6 @@ package ante import ( "errors" - "strconv" errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" @@ -53,9 +52,6 @@ func (esc EthSetupContextDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul WithKVGasConfig(storetypes.GasConfig{}). WithTransientKVGasConfig(storetypes.GasConfig{}) - // Reset transient gas used to prepare the execution of current cosmos tx. - // Transient gas-used is necessary to sum the gas-used of cosmos tx, when it contains multiple eth msgs. - esc.evmKeeper.ResetTransientGasUsed(ctx) return next(newCtx, tx, simulate) } @@ -73,8 +69,7 @@ func NewEthEmitEventDecorator(evmKeeper EVMKeeper) EthEmitEventDecorator { func (eeed EthEmitEventDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { // After eth tx passed ante handler, the fee is deducted and nonce increased, it shouldn't be ignored by json-rpc, // we need to emit some basic events at the very end of ante handler to be indexed by tendermint. - txIndex := eeed.evmKeeper.GetTxIndexTransient(ctx) - for i, msg := range tx.GetMsgs() { + for _, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil)) @@ -85,7 +80,6 @@ func (eeed EthEmitEventDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat ctx.EventManager().EmitEvent(sdk.NewEvent( evmtypes.EventTypeEthereumTx, sdk.NewAttribute(evmtypes.AttributeKeyEthereumTxHash, msgEthTx.Hash), - sdk.NewAttribute(evmtypes.AttributeKeyTxIndex, strconv.FormatUint(txIndex+uint64(i), 10)), //#nosec G115 )) } diff --git a/app/app.go b/app/app.go index 8fc0236ca0..d00c2eefff 100644 --- a/app/app.go +++ b/app/app.go @@ -330,9 +330,9 @@ func NewEthermintApp( } // Add the EVM transient store key - tkeys := storetypes.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey) + tkeys := storetypes.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) - okeys := storetypes.NewObjectStoreKeys(banktypes.ObjectStoreKey) + okeys := storetypes.NewObjectStoreKeys(banktypes.ObjectStoreKey, evmtypes.ObjectStoreKey) app := &EthermintApp{ BaseApp: bApp, legacyAmino: legacyAmino, @@ -576,7 +576,7 @@ func NewEthermintApp( app.EvmKeeper = evmkeeper.NewKeeper( appCodec, runtime.NewKVStoreService(keys[evmtypes.StoreKey]), - tkeys[evmtypes.TransientKey], + okeys[evmtypes.ObjectStoreKey], authtypes.NewModuleAddress(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, diff --git a/tests/importer/importer_test.go b/tests/importer/importer_test.go index 285300b265..8d5de1936a 100644 --- a/tests/importer/importer_test.go +++ b/tests/importer/importer_test.go @@ -148,10 +148,9 @@ func (suite *ImporterTestSuite) TestImportBlocks() { applyDAOHardFork(vmdb) } - for _, tx := range block.Transactions() { - + for i, tx := range block.Transactions() { receipt, gas, err := applyTransaction( - ctx, chainConfig, chainContext, nil, gp, suite.app.EvmKeeper, vmdb, header, tx, usedGas, vmConfig, + ctx, chainConfig, chainContext, nil, gp, suite.app.EvmKeeper, vmdb, header, tx, usedGas, vmConfig, uint(i), ) suite.Require().NoError(err, "failed to apply tx at block %d; tx: %X; gas %d; receipt:%v", block.NumberU64(), tx.Hash(), gas, receipt) suite.Require().NotNil(receipt) @@ -230,7 +229,7 @@ func applyDAOHardFork(vmdb ethvm.StateDB) { func applyTransaction( ctx sdk.Context, config *ethparams.ChainConfig, bc ethcore.ChainContext, author *common.Address, gp *ethcore.GasPool, evmKeeper *evmkeeper.Keeper, vmdb *statedb.StateDB, header *ethtypes.Header, - tx *ethtypes.Transaction, usedGas *uint64, cfg ethvm.Config, + tx *ethtypes.Transaction, usedGas *uint64, cfg ethvm.Config, index uint, ) (*ethtypes.Receipt, uint64, error) { msg, err := tx.AsMessage(ethtypes.MakeSigner(config, header.Number), sdkmath.ZeroInt().BigInt()) if err != nil { @@ -271,7 +270,7 @@ func applyTransaction( receipt.Bloom = ethtypes.CreateBloom(ethtypes.Receipts{receipt}) receipt.BlockHash = header.Hash() receipt.BlockNumber = header.Number - receipt.TransactionIndex = uint(evmKeeper.GetTxIndexTransient(ctx)) + receipt.TransactionIndex = index return receipt, execResult.UsedGas, err } diff --git a/types/int.go b/types/int.go index 4098d4105d..908a253134 100644 --- a/types/int.go +++ b/types/int.go @@ -36,6 +36,13 @@ func SafeInt64(value uint64) (int64, error) { return int64(value), nil //#nosec G115 } +func SafeIntToUint64(value int) (uint64, error) { + if value < 0 { + return 0, fmt.Errorf("invalid value: %d", value) + } + return uint64(value), nil +} + // SafeNewIntFromBigInt constructs Int from big.Int, return error if more than 256bits func SafeNewIntFromBigInt(i *big.Int) (sdkmath.Int, error) { if !IsValidInt256(i) { diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index cb00b761a8..83941e36de 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -18,10 +18,7 @@ package keeper import ( "context" - storetypes "cosmossdk.io/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - - ethtypes "github.com/ethereum/go-ethereum/core/types" ) // BeginBlock sets the sdk Context and EIP155 chain id to the Keeper. @@ -37,11 +34,7 @@ func (k *Keeper) BeginBlock(ctx context.Context) error { func (k *Keeper) EndBlock(ctx context.Context) error { sdkCtx := sdk.UnwrapSDKContext(ctx) - // Gas costs are handled within msg handler so costs should be ignored - infCtx := sdkCtx.WithGasMeter(storetypes.NewInfiniteGasMeter()) - - bloom := ethtypes.BytesToBloom(k.GetBlockBloomTransient(infCtx).Bytes()) - k.EmitBlockBloomEvent(infCtx, bloom) + k.CollectTxBloom(sdkCtx) return nil } diff --git a/x/evm/keeper/bloom.go b/x/evm/keeper/bloom.go new file mode 100644 index 0000000000..193e20a1b9 --- /dev/null +++ b/x/evm/keeper/bloom.go @@ -0,0 +1,27 @@ +package keeper + +import ( + "math/big" + + "cosmossdk.io/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/evmos/ethermint/x/evm/types" +) + +func (k Keeper) SetTxBloom(ctx sdk.Context, bloom *big.Int) { + store := ctx.ObjectStore(k.objectKey) + store.Set(types.ObjectBloomKey(ctx.TxIndex(), ctx.MsgIndex()), bloom) +} + +func (k Keeper) CollectTxBloom(ctx sdk.Context) { + store := prefix.NewObjStore(ctx.ObjectStore(k.objectKey), types.KeyPrefixObjectBloom) + it := store.Iterator(nil, nil) + defer it.Close() + + bloom := new(big.Int) + for ; it.Valid(); it.Next() { + bloom.Or(bloom, it.Value().(*big.Int)) + } + + k.EmitBlockBloomEvent(ctx, bloom.Bytes()) +} diff --git a/x/evm/keeper/config.go b/x/evm/keeper/config.go index 584463a56e..a7a558105c 100644 --- a/x/evm/keeper/config.go +++ b/x/evm/keeper/config.go @@ -52,8 +52,7 @@ func (k *Keeper) TxConfig(ctx sdk.Context, txHash common.Hash) statedb.TxConfig return statedb.NewTxConfig( common.BytesToHash(ctx.HeaderHash()), // BlockHash txHash, // TxHash - uint(k.GetTxIndexTransient(ctx)), // TxIndex - uint(k.GetLogSizeTransient(ctx)), // LogIndex + 0, 0, ) } diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 383fb222c2..e4d1111113 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -22,7 +22,6 @@ import ( errorsmod "cosmossdk.io/errors" "cosmossdk.io/log" sdkmath "cosmossdk.io/math" - "cosmossdk.io/store/prefix" storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -50,8 +49,8 @@ type Keeper struct { // - storing Bloom filters by block height. Needed for the Web3 API. storeService store.KVStoreService - // key to access the transient store, which is reset on every block during Commit - transientKey storetypes.StoreKey + // key to access the object store, which is reset on every block during Commit + objectKey storetypes.StoreKey // the address capable of executing a MsgUpdateParams message. Typically, this should be the x/gov module account. authority sdk.AccAddress @@ -86,7 +85,7 @@ type Keeper struct { func NewKeeper( cdc codec.BinaryCodec, storeService store.KVStoreService, - transientKey storetypes.StoreKey, + objectKey storetypes.StoreKey, authority sdk.AccAddress, ak types.AccountKeeper, bankKeeper types.BankKeeper, @@ -116,7 +115,7 @@ func NewKeeper( stakingKeeper: sk, feeMarketKeeper: fmk, storeService: storeService, - transientKey: transientKey, + objectKey: objectKey, customPrecompiles: customPrecompiles, evmConstructor: evmConstructor, tracer: tracer, @@ -154,11 +153,11 @@ func (k Keeper) ChainID() *big.Int { // ---------------------------------------------------------------------------- // EmitBlockBloomEvent emit block bloom events -func (k Keeper) EmitBlockBloomEvent(ctx sdk.Context, bloom ethtypes.Bloom) { +func (k Keeper) EmitBlockBloomEvent(ctx sdk.Context, bloom []byte) { ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeBlockBloom, - sdk.NewAttribute(types.AttributeKeyEthereumBloom, string(bloom.Bytes())), + sdk.NewAttribute(types.AttributeKeyEthereumBloom, string(bloom)), ), ) } @@ -168,69 +167,6 @@ func (k Keeper) GetAuthority() sdk.AccAddress { return k.authority } -// GetBlockBloomTransient returns bloom bytes for the current block height -func (k Keeper) GetBlockBloomTransient(ctx sdk.Context) *big.Int { - store := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientBloom) - heightBz := sdk.Uint64ToBigEndian(uint64(ctx.BlockHeight())) //#nosec G115 - bz := store.Get(heightBz) - if len(bz) == 0 { - return big.NewInt(0) - } - - return new(big.Int).SetBytes(bz) -} - -// SetBlockBloomTransient sets the given bloom bytes to the transient store. This value is reset on -// every block. -func (k Keeper) SetBlockBloomTransient(ctx sdk.Context, bloom *big.Int) { - store := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientBloom) - heightBz := sdk.Uint64ToBigEndian(uint64(ctx.BlockHeight())) //#nosec G115 - store.Set(heightBz, bloom.Bytes()) -} - -// ---------------------------------------------------------------------------- -// Tx -// ---------------------------------------------------------------------------- - -// SetTxIndexTransient set the index of processing transaction -func (k Keeper) SetTxIndexTransient(ctx sdk.Context, index uint64) { - store := ctx.TransientStore(k.transientKey) - store.Set(types.KeyPrefixTransientTxIndex, sdk.Uint64ToBigEndian(index)) -} - -// GetTxIndexTransient returns EVM transaction index on the current block. -func (k Keeper) GetTxIndexTransient(ctx sdk.Context) uint64 { - store := ctx.TransientStore(k.transientKey) - bz := store.Get(types.KeyPrefixTransientTxIndex) - if len(bz) == 0 { - return 0 - } - - return sdk.BigEndianToUint64(bz) -} - -// ---------------------------------------------------------------------------- -// Log -// ---------------------------------------------------------------------------- - -// GetLogSizeTransient returns EVM log index on the current block. -func (k Keeper) GetLogSizeTransient(ctx sdk.Context) uint64 { - store := ctx.TransientStore(k.transientKey) - bz := store.Get(types.KeyPrefixTransientLogSize) - if len(bz) == 0 { - return 0 - } - - return sdk.BigEndianToUint64(bz) -} - -// SetLogSizeTransient fetches the current EVM log index from the transient store, increases its -// value by one and then sets the new index back to the transient store. -func (k Keeper) SetLogSizeTransient(ctx sdk.Context, logSize uint64) { - store := ctx.TransientStore(k.transientKey) - store.Set(types.KeyPrefixTransientLogSize, sdk.Uint64ToBigEndian(logSize)) -} - // ---------------------------------------------------------------------------- // Storage // ---------------------------------------------------------------------------- @@ -367,27 +303,20 @@ func (k Keeper) GetMinGasMultiplier(ctx sdk.Context) sdkmath.LegacyDec { return fmkParmas.MinGasMultiplier } -// ResetTransientGasUsed reset gas used to prepare for execution of current cosmos tx, called in ante handler. -func (k Keeper) ResetTransientGasUsed(ctx sdk.Context) { - store := ctx.TransientStore(k.transientKey) - store.Delete(types.KeyPrefixTransientGasUsed) -} - // GetTransientGasUsed returns the gas used by current cosmos tx. func (k Keeper) GetTransientGasUsed(ctx sdk.Context) uint64 { - store := ctx.TransientStore(k.transientKey) - bz := store.Get(types.KeyPrefixTransientGasUsed) - if len(bz) == 0 { + store := ctx.ObjectStore(k.objectKey) + v := store.Get(types.ObjectGasUsedKey(ctx.TxIndex())) + if v == nil { return 0 } - return sdk.BigEndianToUint64(bz) + return v.(uint64) } // SetTransientGasUsed sets the gas used by current cosmos tx. func (k Keeper) SetTransientGasUsed(ctx sdk.Context, gasUsed uint64) { - store := ctx.TransientStore(k.transientKey) - bz := sdk.Uint64ToBigEndian(gasUsed) - store.Set(types.KeyPrefixTransientGasUsed, bz) + store := ctx.ObjectStore(k.objectKey) + store.Set(types.ObjectGasUsedKey(ctx.TxIndex()), gasUsed) } // AddTransientGasUsed accumulate gas used by each eth msgs included in current cosmos tx. diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index 40c2d09c4d..66eb4896e2 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -45,7 +45,6 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t ctx := sdk.UnwrapSDKContext(goCtx) tx := msg.AsTransaction() - txIndex := k.GetTxIndexTransient(ctx) labels := []metrics.Label{ telemetry.NewLabel("tx_type", fmt.Sprintf("%d", tx.Type())), @@ -93,8 +92,6 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t sdk.NewAttribute(sdk.AttributeKeyAmount, tx.Value().String()), // add event for ethereum transaction hash format sdk.NewAttribute(types.AttributeKeyEthereumTxHash, response.Hash), - // add event for index of valid ethereum tx - sdk.NewAttribute(types.AttributeKeyTxIndex, strconv.FormatUint(txIndex, 10)), // add event for eth tx gas used, we can't get it from cosmos tx result when it contains multiple eth tx msgs. sdk.NewAttribute(types.AttributeKeyTxGasUsed, strconv.FormatUint(response.GasUsed, 10)), } diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index e205c56d9a..ba2d4f3c20 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -166,11 +166,6 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { // // For relevant discussion see: https://github.com/cosmos/cosmos-sdk/discussions/9072 func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) (*types.MsgEthereumTxResponse, error) { - var ( - bloom *big.Int - bloomReceipt ethtypes.Bloom - ) - cfg, err := k.EVMConfig(ctx, sdk.ConsAddress(ctx.BlockHeader().ProposerAddress), k.eip155ChainID) if err != nil { return nil, errorsmod.Wrap(err, "failed to load evm config") @@ -204,9 +199,7 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) // Compute block bloom filter if len(logs) > 0 { - bloom = k.GetBlockBloomTransient(ctx) - bloom.Or(bloom, big.NewInt(0).SetBytes(ethtypes.LogsBloom(logs))) - bloomReceipt = ethtypes.BytesToBloom(bloom.Bytes()) + k.SetTxBloom(tmpCtx, new(big.Int).SetBytes(ethtypes.LogsBloom(logs))) } var contractAddr common.Address @@ -215,16 +208,14 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) } receipt := ðtypes.Receipt{ - Type: ethTx.Type(), - PostState: nil, // TODO: intermediate state root - Bloom: bloomReceipt, - Logs: logs, - TxHash: txConfig.TxHash, - ContractAddress: contractAddr, - GasUsed: res.GasUsed, - BlockHash: txConfig.BlockHash, - BlockNumber: big.NewInt(ctx.BlockHeight()), - TransactionIndex: txConfig.TxIndex, + Type: ethTx.Type(), + PostState: nil, // TODO: intermediate state root + Logs: logs, + TxHash: txConfig.TxHash, + ContractAddress: contractAddr, + GasUsed: res.GasUsed, + BlockHash: txConfig.BlockHash, + BlockNumber: big.NewInt(ctx.BlockHeight()), } if !res.Failed() { @@ -251,14 +242,6 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) return nil, errorsmod.Wrapf(err, "failed to refund gas leftover gas to sender %s", msg.From()) } - if len(receipt.Logs) > 0 { - // Update transient block bloom filter - k.SetBlockBloomTransient(ctx, receipt.Bloom.Big()) - k.SetLogSizeTransient(ctx, uint64(txConfig.LogIndex)+uint64(len(receipt.Logs))) - } - - k.SetTxIndexTransient(ctx, uint64(txConfig.TxIndex)+1) - totalGasUsed, err := k.AddTransientGasUsed(ctx, res.GasUsed) if err != nil { return nil, errorsmod.Wrap(err, "failed to add transient gas used") diff --git a/x/evm/migrations/v4/migrate_test.go b/x/evm/migrations/v4/migrate_test.go index 5c56925123..4e17e574df 100644 --- a/x/evm/migrations/v4/migrate_test.go +++ b/x/evm/migrations/v4/migrate_test.go @@ -33,7 +33,7 @@ func TestMigrate(t *testing.T) { cdc := encCfg.Codec storeKey := storetypes.NewKVStoreKey(types.ModuleName) - tKey := storetypes.NewTransientStoreKey(types.TransientKey) + tKey := storetypes.NewTransientStoreKey("transient_test") ctx := testutil.DefaultContext(storeKey, tKey) storeService := runtime.NewKVStoreService(storeKey) diff --git a/x/evm/types/key.go b/x/evm/types/key.go index f8d768fc85..b3b7173ba9 100644 --- a/x/evm/types/key.go +++ b/x/evm/types/key.go @@ -16,7 +16,10 @@ package types import ( + "encoding/binary" + "github.com/ethereum/go-ethereum/common" + ethermint "github.com/evmos/ethermint/types" ) const ( @@ -28,9 +31,9 @@ const ( // The EVM module should use a prefix store. StoreKey = ModuleName - // TransientKey is the key to access the EVM transient store, that is reset + // ObjectStoreKey is the key to access the EVM object store, that is reset // during the Commit phase. - TransientKey = "transient_" + ModuleName + ObjectStoreKey = "object:" + ModuleName // RouterKey uses module name for routing RouterKey = ModuleName @@ -43,12 +46,11 @@ const ( prefixParams ) -// prefix bytes for the EVM transient store +// prefix bytes for the EVM object store const ( - prefixTransientBloom = iota + 1 - prefixTransientTxIndex - prefixTransientLogSize - prefixTransientGasUsed + prefixObjectBloom = iota + 1 + prefixObjectGasUsed + prefixObjectParams ) // KVStore key prefixes @@ -58,12 +60,12 @@ var ( KeyPrefixParams = []byte{prefixParams} ) -// Transient Store key prefixes +// Object Store key prefixes var ( - KeyPrefixTransientBloom = []byte{prefixTransientBloom} - KeyPrefixTransientTxIndex = []byte{prefixTransientTxIndex} - KeyPrefixTransientLogSize = []byte{prefixTransientLogSize} - KeyPrefixTransientGasUsed = []byte{prefixTransientGasUsed} + KeyPrefixObjectBloom = []byte{prefixObjectBloom} + KeyPrefixObjectGasUsed = []byte{prefixObjectGasUsed} + // cache the `EVMBlockConfig` during the whole block execution + KeyPrefixObjectParams = []byte{prefixObjectParams} ) // AddressStoragePrefix returns a prefix to iterate over a given account storage. @@ -75,3 +77,36 @@ func AddressStoragePrefix(address common.Address) []byte { func StateKey(address common.Address, key []byte) []byte { return append(AddressStoragePrefix(address), key...) } + +func ObjectGasUsedKey(txIndex int) []byte { + var key [1 + 8]byte + key[0] = prefixObjectGasUsed + if txIndex < 0 { + return key[:] + } + idx, err := ethermint.SafeIntToUint64(txIndex) + if err != nil { + panic(err) + } + binary.BigEndian.PutUint64(key[1:], idx) + return key[:] +} + +func ObjectBloomKey(txIndex, msgIndex int) []byte { + var key [1 + 8 + 8]byte + key[0] = prefixObjectBloom + if txIndex < 0 || msgIndex < 0 { + return key[:] + } + value, err := ethermint.SafeIntToUint64(txIndex) + if err != nil { + panic(err) + } + binary.BigEndian.PutUint64(key[1:], value) + value, err = ethermint.SafeIntToUint64(msgIndex) + if err != nil { + panic(err) + } + binary.BigEndian.PutUint64(key[9:], value) + return key[:] +}