Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
evm: use block proposer address for COINBASE opcode (#291)
Browse files Browse the repository at this point in the history
* evm: use block proposer address for COINBASE opcode

* test

* c++
  • Loading branch information
fedekunze committed Jul 14, 2021
1 parent 5e87661 commit 9d1ce30
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Bug Fixes

* (evm) [tharsis#291](https://github.com/tharsis/ethermint/pull/291) Use block proposer address (validator operator) for `COINBASE` opcode.
* (rpc) [tharsis#81](https://github.com/tharsis/ethermint/pull/81) Fix transaction hashing and decoding on `eth_sendTransaction`.
* (rpc) [tharsis#45](https://github.com/tharsis/ethermint/pull/45) Use `EmptyUncleHash` and `EmptyRootHash` for empty ethereum `Header` fields.

Expand Down
6 changes: 4 additions & 2 deletions app/ante/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ type EVMKeeper interface {
GetParams(ctx sdk.Context) evmtypes.Params
WithContext(ctx sdk.Context)
ResetRefundTransient(ctx sdk.Context)
NewEVM(msg core.Message, config *params.ChainConfig, params evmtypes.Params) *vm.EVM
GetCoinbaseAddress() (common.Address, error)
NewEVM(msg core.Message, config *params.ChainConfig, params evmtypes.Params, coinbase common.Address) *vm.EVM
GetCodeHash(addr common.Address) common.Hash
}

Expand Down Expand Up @@ -388,7 +389,8 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
)
}

evm := ctd.evmKeeper.NewEVM(coreMsg, ethCfg, params)
// NOTE: pass in an empty coinbase address as we don't need it for the check below
evm := ctd.evmKeeper.NewEVM(coreMsg, ethCfg, params, common.Address{})

// check that caller has enough balance to cover asset transfer for **topmost** call
// NOTE: here the gas consumed is from the context with the infinite gas meter
Expand Down
7 changes: 6 additions & 1 deletion x/evm/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,12 @@ func (k Keeper) EthCall(c context.Context, req *types.EthCallRequest) (*types.Ms
params := k.GetParams(ctx)
ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID)

evm := k.NewEVM(msg, ethCfg, params)
coinbase, err := k.GetCoinbaseAddress()
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

evm := k.NewEVM(msg, ethCfg, params, coinbase)
res, err := k.ApplyMessage(evm, msg, ethCfg)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
Expand Down
32 changes: 28 additions & 4 deletions x/evm/keeper/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

ethermint "github.com/tharsis/ethermint/types"
"github.com/tharsis/ethermint/x/evm/types"
Expand All @@ -24,13 +25,15 @@ import (
"github.com/ethereum/go-ethereum/params"
)

// NewEVM generates an ethereum VM from the provided Message fields and the ChainConfig.
func (k *Keeper) NewEVM(msg core.Message, config *params.ChainConfig, params types.Params) *vm.EVM {
// NewEVM generates an ethereum VM from the provided Message fields and the chain parameters
// (config). It sets the validator operator address as the coinbase address to make it available for
// the COINBASE opcode, even though there is no beneficiary (since we're not mining).
func (k *Keeper) NewEVM(msg core.Message, config *params.ChainConfig, params types.Params, coinbase common.Address) *vm.EVM {
blockCtx := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: k.GetHashFn(),
Coinbase: common.Address{}, // there's no beneficiary since we're not mining
Coinbase: coinbase,
GasLimit: ethermint.BlockGasLimit(k.ctx),
BlockNumber: big.NewInt(k.ctx.BlockHeight()),
Time: big.NewInt(k.ctx.BlockHeader().Time.Unix()),
Expand Down Expand Up @@ -127,7 +130,13 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
cacheCtx, commit := k.ctx.CacheContext()
k.ctx = cacheCtx

evm := k.NewEVM(msg, ethCfg, params)
// get the coinbase address from the block proposer
coinbase, err := k.GetCoinbaseAddress()
if err != nil {
return nil, stacktrace.Propagate(err, "failed to obtain coinbase address")
}

evm := k.NewEVM(msg, ethCfg, params, coinbase)

k.SetTxHashTransient(tx.Hash())
k.IncreaseTxIndexTransient()
Expand Down Expand Up @@ -325,3 +334,18 @@ func (k *Keeper) resetGasMeterAndConsumeGas(gasUsed uint64) {
k.ctx.GasMeter().RefundGas(k.ctx.GasMeter().GasConsumed(), "reset the gas count")
k.ctx.GasMeter().ConsumeGas(gasUsed, "apply evm transaction")
}

// GetCoinbaseAddress returns the block proposer's validator operator address.
func (k Keeper) GetCoinbaseAddress() (common.Address, error) {
consAddr := sdk.ConsAddress(k.ctx.BlockHeader().ProposerAddress)
validator, found := k.stakingKeeper.GetValidatorByConsAddr(k.ctx, consAddr)
if !found {
return common.Address{}, stacktrace.Propagate(
sdkerrors.Wrap(stakingtypes.ErrNoValidatorFound, consAddr.String()),
"failed to retrieve validator from block proposer address",
)
}

coinbase := common.BytesToAddress(validator.GetOperator())
return coinbase, nil
}
73 changes: 73 additions & 0 deletions x/evm/keeper/state_transition_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package keeper_test

import (
"fmt"

"github.com/tharsis/ethermint/tests"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

func (suite *KeeperTestSuite) TestGetCoinbaseAddress() {
valOpAddr := tests.GenerateAddress()

testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"validator not found",
func() {},
false,
},
{
"success",
func() {
valConsAddr, privkey := tests.NewAddrKey()

pkAny, err := codectypes.NewAnyWithValue(privkey.PubKey())
suite.Require().NoError(err)

validator := stakingtypes.Validator{
OperatorAddress: sdk.ValAddress(valOpAddr.Bytes()).String(),
ConsensusPubkey: pkAny,
}

suite.app.StakingKeeper.SetValidator(suite.ctx, validator)
err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
suite.Require().NoError(err)

header := suite.ctx.BlockHeader()
header.ProposerAddress = valConsAddr.Bytes()
suite.ctx = suite.ctx.WithBlockHeader(header)

validator, found := suite.app.StakingKeeper.GetValidatorByConsAddr(suite.ctx, valConsAddr.Bytes())
suite.Require().True(found)

suite.app.EvmKeeper.WithContext(suite.ctx)
suite.Require().NotEmpty(suite.ctx.BlockHeader().ProposerAddress)
},
true,
},
}

for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset

tc.malleate()

coinbase, err := suite.app.EvmKeeper.GetCoinbaseAddress()
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(valOpAddr, coinbase)
} else {
suite.Require().Error(err)
}
})
}

}

0 comments on commit 9d1ce30

Please sign in to comment.