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

Commit

Permalink
rpc, evm: fix eth_coinbase endpoint to return ethereum address of t…
Browse files Browse the repository at this point in the history
…he validator node (#153)

* fix coinbase rpc endpoint to return ethereum address of the validator

* update changelog

* fix lint

* clean code and simplify logic

* fix changelog

* change request variable name and type

* add test

* fix proto comments

* fix proto comments

* Update x/evm/keeper/grpc_query.go

* Update x/evm/keeper/grpc_query.go

Co-authored-by: Federico Kunze <[email protected]>
  • Loading branch information
thomas-nguy and fedekunze authored Jun 22, 2021
1 parent 4ca7c43 commit fada595
Show file tree
Hide file tree
Showing 11 changed files with 857 additions and 91 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (evm) [tharsis#66](https://github.com/tharsis/ethermint/issues/66) Support legacy transaction types for signing.
* (evm) [tharsis#24](https://github.com/tharsis/ethermint/pull/24) Implement metrics for `MsgEthereumTx`, state transitions, `BeginBlock` and `EndBlock`.
* (rpc) [#124](https://github.com/tharsis/ethermint/issues/124) Implement `txpool_content`, `txpool_inspect` and `txpool_status` RPC methods
* (rpc) [tharsis#112](https://github.com/tharsis/ethermint/pull/153) Fix `eth_coinbase` to return the ethereum address of the validator

### Bug Fixes

Expand Down
15 changes: 14 additions & 1 deletion docs/basics/json_rpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Check the JSON-RPC methods and namespaces supported on Ethermint. {synopsis}
| `eth_compileSerpent` | Eth | | |
| `eth_signTransaction` | Eth | | |
| `eth_mining` | Eth | N/A | Not relevant to Ethermint |
| `eth_coinbase` | Eth | N/A | Not relevant to Ethermint |
| [`eth_coinbase`](#eth-coinbase) | Eth | | |
| `eth_hashrate` | Eth | N/A | Not relevant to Ethermint |
| `eth_getUncleCountByBlockHash` | Eth | N/A | Not relevant to Ethermint |
| `eth_getUncleCountByBlockNumber` | Eth | N/A | Not relevant to Ethermint |
Expand Down Expand Up @@ -731,6 +731,19 @@ curl -X POST --data '{"jsonrpc":"2.0","method":"txpool_status","params":[],"id":
{"jsonrpc":"2.0","id":1,"result":{"pending":"0x0","queued":"0x0"}}
```

### eth_coinbase

Returns the account the mining rewards will be send to.


```json
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_coinbase","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545

// Result
{"jsonrpc":"2.0","id":1,"result":"0x7cB61D4117AE31a12E393a1Cfa3BaC666481D02E"}
```

## WebSocket Methods

Read about websockets in [events](./../quickstart/events.md) {hide}
Expand Down
37 changes: 36 additions & 1 deletion docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
- [QueryStorageResponse](#ethermint.evm.v1alpha1.QueryStorageResponse)
- [QueryTxLogsRequest](#ethermint.evm.v1alpha1.QueryTxLogsRequest)
- [QueryTxLogsResponse](#ethermint.evm.v1alpha1.QueryTxLogsResponse)
- [QueryValidatorAccountRequest](#ethermint.evm.v1alpha1.QueryValidatorAccountRequest)
- [QueryValidatorAccountResponse](#ethermint.evm.v1alpha1.QueryValidatorAccountResponse)

- [Query](#ethermint.evm.v1alpha1.Query)

Expand Down Expand Up @@ -704,6 +706,38 @@ QueryTxLogs is the response type for the Query/TxLogs RPC method.




<a name="ethermint.evm.v1alpha1.QueryValidatorAccountRequest"></a>

### QueryValidatorAccountRequest
QueryValidatorAccountRequest is the request type for the Query/ValidatorAccount RPC method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `cons_address` | [string](#string) | | cons_address is the validator cons address to query the account for. |






<a name="ethermint.evm.v1alpha1.QueryValidatorAccountResponse"></a>

### QueryValidatorAccountResponse
QueryValidatorAccountResponse is the response type for the Query/ValidatorAccount RPC method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `account_address` | [string](#string) | | account_address is the cosmos address of the account in bech32 format. |
| `sequence` | [uint64](#uint64) | | sequence is the account's sequence number. |
| `account_number` | [uint64](#uint64) | | account_number is the account number |





<!-- end messages -->

<!-- end enums -->
Expand All @@ -719,7 +753,8 @@ Query defines the gRPC querier service.
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `Account` | [QueryAccountRequest](#ethermint.evm.v1alpha1.QueryAccountRequest) | [QueryAccountResponse](#ethermint.evm.v1alpha1.QueryAccountResponse) | Account queries an Ethereum account. | GET|/ethermint/evm/v1alpha1/account/{address}|
| `CosmosAccount` | [QueryCosmosAccountRequest](#ethermint.evm.v1alpha1.QueryCosmosAccountRequest) | [QueryCosmosAccountResponse](#ethermint.evm.v1alpha1.QueryCosmosAccountResponse) | Account queries an Ethereum account's Cosmos Address. | GET|/ethermint/evm/v1alpha1/cosmos_account/{address}|
| `CosmosAccount` | [QueryCosmosAccountRequest](#ethermint.evm.v1alpha1.QueryCosmosAccountRequest) | [QueryCosmosAccountResponse](#ethermint.evm.v1alpha1.QueryCosmosAccountResponse) | CosmosAccount queries an Ethereum account's Cosmos Address. | GET|/ethermint/evm/v1alpha1/cosmos_account/{address}|
| `ValidatorAccount` | [QueryValidatorAccountRequest](#ethermint.evm.v1alpha1.QueryValidatorAccountRequest) | [QueryValidatorAccountResponse](#ethermint.evm.v1alpha1.QueryValidatorAccountResponse) | ValidatorAccount queries an Ethereum account's from a validator consensus Address. | GET|/ethermint/evm/v1alpha1/validator_account/{cons_address}|
| `Balance` | [QueryBalanceRequest](#ethermint.evm.v1alpha1.QueryBalanceRequest) | [QueryBalanceResponse](#ethermint.evm.v1alpha1.QueryBalanceResponse) | Balance queries the balance of a the EVM denomination for a single EthAccount. | GET|/ethermint/evm/v1alpha1/balances/{address}|
| `Storage` | [QueryStorageRequest](#ethermint.evm.v1alpha1.QueryStorageRequest) | [QueryStorageResponse](#ethermint.evm.v1alpha1.QueryStorageResponse) | Storage queries the balance of all coins for a single account. | GET|/ethermint/evm/v1alpha1/storage/{address}/{key}|
| `Code` | [QueryCodeRequest](#ethermint.evm.v1alpha1.QueryCodeRequest) | [QueryCodeResponse](#ethermint.evm.v1alpha1.QueryCodeResponse) | Code queries the balance of all coins for a single account. | GET|/ethermint/evm/v1alpha1/codes/{address}|
Expand Down
19 changes: 15 additions & 4 deletions ethereum/rpc/eth_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,20 +129,31 @@ func (e *PublicEthAPI) Syncing() (interface{}, error) {
}

// Coinbase is the address that staking rewards will be send to (alias for Etherbase).
func (e *PublicEthAPI) Coinbase() (common.Address, error) {
func (e *PublicEthAPI) Coinbase() (string, error) {
e.logger.Debugln("eth_coinbase")

node, err := e.clientCtx.GetNode()
if err != nil {
return common.Address{}, err
return "", err
}

status, err := node.Status(e.ctx)
if err != nil {
return common.Address{}, err
return "", err
}

return common.BytesToAddress(status.ValidatorInfo.Address.Bytes()), nil
req := &evmtypes.QueryValidatorAccountRequest{
ConsAddress: sdk.ConsAddress(status.ValidatorInfo.Address).String(),
}

res, err := e.queryClient.ValidatorAccount(e.ctx, req)
if err != nil {
return "", err
}

toAddr, _ := sdk.AccAddressFromBech32(res.AccountAddress)
ethAddr := common.BytesToAddress(toAddr.Bytes())
return ethAddr.Hex(), nil
}

// Mining returns whether or not this node is currently mining. Always false.
Expand Down
26 changes: 25 additions & 1 deletion proto/ethermint/evm/v1alpha1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ service Query {
option (google.api.http).get = "/ethermint/evm/v1alpha1/account/{address}";
}

// Account queries an Ethereum account's Cosmos Address.
// CosmosAccount queries an Ethereum account's Cosmos Address.
rpc CosmosAccount(QueryCosmosAccountRequest) returns (QueryCosmosAccountResponse) {
option (google.api.http).get = "/ethermint/evm/v1alpha1/cosmos_account/{address}";
}

// ValidatorAccount queries an Ethereum account's from a validator consensus Address.
rpc ValidatorAccount(QueryValidatorAccountRequest) returns (QueryValidatorAccountResponse) {
option (google.api.http).get = "/ethermint/evm/v1alpha1/validator_account/{cons_address}";
}

// Balance queries the balance of a the EVM denomination for a single
// EthAccount.
rpc Balance(QueryBalanceRequest) returns (QueryBalanceResponse) {
Expand Down Expand Up @@ -100,6 +105,25 @@ message QueryCosmosAccountResponse {
uint64 account_number = 3;
}

// QueryValidatorAccountRequest is the request type for the Query/ValidatorAccount RPC method.
message QueryValidatorAccountRequest {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

// cons_address is the validator cons address to query the account for.
string cons_address = 1;
}

// QueryValidatorAccountResponse is the response type for the Query/ValidatorAccount RPC method.
message QueryValidatorAccountResponse {
// account_address is the cosmos address of the account in bech32 format.
string account_address = 1;
// sequence is the account's sequence number.
uint64 sequence = 2;
// account_number is the account number
uint64 account_number = 3;
}

// QueryBalanceRequest is the request type for the Query/Balance RPC method.
message QueryBalanceRequest {
option (gogoproto.equal) = false;
Expand Down
36 changes: 36 additions & 0 deletions x/evm/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,42 @@ func (k Keeper) CosmosAccount(c context.Context, req *types.QueryCosmosAccountRe
return &res, nil
}

func (k Keeper) ValidatorAccount(c context.Context, req *types.QueryValidatorAccountRequest) (*types.QueryValidatorAccountResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

consAddr, err := sdk.ConsAddressFromBech32(req.ConsAddress)
if err != nil {
return nil, status.Error(
codes.InvalidArgument, err.Error(),
)
}

ctx := sdk.UnwrapSDKContext(c)
k.WithContext(ctx)

validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
if !found {
return nil, nil
}

accAddr := sdk.AccAddress(validator.GetOperator())

res := types.QueryValidatorAccountResponse{
AccountAddress: accAddr.String(),
}

account := k.accountKeeper.GetAccount(ctx, accAddr)
if account != nil {
res.Sequence = account.GetSequence()
res.AccountNumber = account.GetAccountNumber()
}

return &res, nil

}

// Balance implements the Query/Balance gRPC method
func (k Keeper) Balance(c context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) {
if req == nil {
Expand Down
78 changes: 78 additions & 0 deletions x/evm/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,3 +585,81 @@ func (suite *KeeperTestSuite) TestQueryParams() {
suite.Require().NoError(err)
suite.Require().Equal(expParams, res.Params)
}

func (suite *KeeperTestSuite) TestQueryValidatorAccount() {
var (
req *types.QueryValidatorAccountRequest
expAccount *types.QueryValidatorAccountResponse
)

testCases := []struct {
msg string
malleate func()
expPass bool
}{
{"zero address",
func() {
suite.app.BankKeeper.SetBalance(suite.ctx, suite.address.Bytes(), ethermint.NewPhotonCoinInt64(0))
expAccount = &types.QueryValidatorAccountResponse{
AccountAddress: sdk.AccAddress(ethcmn.Address{}.Bytes()).String(),
}
req = &types.QueryValidatorAccountRequest{
ConsAddress: "",
}
},
false,
},
{
"success",
func() {
expAccount = &types.QueryValidatorAccountResponse{
AccountAddress: sdk.AccAddress(suite.address.Bytes()).String(),
Sequence: 0,
AccountNumber: 0,
}
req = &types.QueryValidatorAccountRequest{
ConsAddress: suite.consAddress.String(),
}
},
true,
},
{
"success with seq and account number",
func() {
acc := suite.app.AccountKeeper.GetAccount(suite.ctx, suite.address.Bytes())
suite.Require().NoError(acc.SetSequence(10))
suite.Require().NoError(acc.SetAccountNumber(1))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

expAccount = &types.QueryValidatorAccountResponse{
AccountAddress: sdk.AccAddress(suite.address.Bytes()).String(),
Sequence: 10,
AccountNumber: 1,
}
req = &types.QueryValidatorAccountRequest{
ConsAddress: suite.consAddress.String(),
}
},
true,
},
}

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

tc.malleate()
ctx := sdk.WrapSDKContext(suite.ctx)
res, err := suite.queryClient.ValidatorAccount(ctx, req)

if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)

suite.Require().Equal(expAccount, res)
} else {
suite.Require().Error(err)
}
})
}
}
11 changes: 11 additions & 0 deletions x/evm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper_test

import (
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
"testing"
"time"

Expand All @@ -9,6 +10,7 @@ import (
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

"github.com/cosmos/ethermint/app"
ethermint "github.com/cosmos/ethermint/types"
Expand All @@ -34,6 +36,7 @@ type KeeperTestSuite struct {
app *app.EthermintApp
queryClient types.QueryClient
address ethcmn.Address
consAddress sdk.ConsAddress
}

func (suite *KeeperTestSuite) SetupTest() {
Expand All @@ -57,6 +60,14 @@ func (suite *KeeperTestSuite) SetupTest() {

suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), balance)

priv, err := ethsecp256k1.GenerateKey()
suite.Require().NoError(err)
valAddr := sdk.ValAddress(suite.address.Bytes())
validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{})
suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
suite.app.StakingKeeper.SetValidator(suite.ctx, validator)
suite.consAddress = sdk.ConsAddress(priv.PubKey().Address())
}

func TestKeeperTestSuite(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions x/evm/types/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ type BankKeeper interface {
// StakingKeeper returns the historical headers kept in store.
type StakingKeeper interface {
GetHistoricalInfo(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool)
GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator stakingtypes.Validator, found bool)
}
Loading

0 comments on commit fada595

Please sign in to comment.