rpc: miner_ namespace (#377)

* miner namespace

* SetGasPrice call

* Added note plus fixed error logging

* Refactor to use the keyring in the miner namespace

* Changed keyring function return

* Added more detailed logs to unsupported functions

* Reverted changes on ethapi and just using a refrence to it on miner

* Creating a transaction

* fix condition

* Error string not capitalized

* suggested changes to setEtherbase

* change log level

* minor changes

* minor changes

* Sending tx to test the endpoint

* get tx nonce

* Using aphoton const and changing the logger to debug

* Using default RPC gas limit constant

* Apply suggestions from code review

Renames and log changes

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* pair programming session

* get gas

* Set gas prices note added

* Setting fess and max gas

* delete unnecessary log

* Apply suggestions from code review

return false in case of error

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Suggested changes applied

* Updated changelog and json_rpc docs

* Update CHANGELOG.md

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update ethereum/rpc/namespaces/miner/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update ethereum/rpc/namespaces/miner/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update ethereum/rpc/namespaces/miner/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update ethereum/rpc/namespaces/miner/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Using the same coin denom as the gas price for the fee

Co-authored-by: ramacarlucho <ramirocarlucho@gmail.com>
Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: Federico Kunze <federico.kunze94@gmail.com>
This commit is contained in:
Guillermo Paoletti 2021-08-04 11:18:22 +02:00 committed by GitHub
parent 9bf3659718
commit 396db9f20a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 299 additions and 25 deletions

View File

@ -76,6 +76,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (rpc) [tharsis#272](https://github.com/tharsis/ethermint/pull/272) do binary search to estimate gas accurately * (rpc) [tharsis#272](https://github.com/tharsis/ethermint/pull/272) do binary search to estimate gas accurately
* (rpc) [#313](https://github.com/tharsis/ethermint/pull/313) Implement internal debug namespace (Not including logger functions nor traces). * (rpc) [#313](https://github.com/tharsis/ethermint/pull/313) Implement internal debug namespace (Not including logger functions nor traces).
* (rpc) [#349](https://github.com/tharsis/ethermint/pull/349) Implement configurable JSON-RPC APIs to manage enabled namespaces. * (rpc) [#349](https://github.com/tharsis/ethermint/pull/349) Implement configurable JSON-RPC APIs to manage enabled namespaces.
* (rpc) [#377](https://github.com/tharsis/ethermint/pull/377) Implement `miner_` namespace. `miner_setEtherbase` and `miner_setGasPrice` are working as intended. All the other calls are not applicable and return `unsupported`.
### Bug Fixes ### Bug Fixes

View File

@ -157,12 +157,13 @@ ethermintd start --evm-rpc.api eth,txpool,personal,net,debug,web3
| `les_latestCheckpoint` | Les | | | | `les_latestCheckpoint` | Les | | |
| `les_getCheckpoint` | Les | | | | `les_getCheckpoint` | Les | | |
| `les_getCheckpointContractAddress` | Les | | | | `les_getCheckpointContractAddress` | Les | | |
| `miner_getHashrate` | Miner | | | | `miner_getHashrate` | Miner | N/A | Not relevant to Ethermint |
| `miner_setExtra` | Miner | | | | `miner_setExtra` | Miner | N/A | Not relevant to Ethermint |
| `miner_setGasPrice` | Miner | | | | `miner_setGasPrice` | Miner | ✔ | |
| `miner_start` | Miner | | | | `miner_start` | Miner | N/A | Not relevant to Ethermint |
| `miner_stop` | Miner | | | | `miner_stop` | Miner | N/A | Not relevant to Ethermint |
| `miner_setEtherbase` | Miner | | | | `miner_setGasLimit` | Miner | N/A | Not relevant to Ethermint |
| `miner_setEtherbase` | Miner | ✔ | |
| `txpool_content` | TXPool | ✔ | | | `txpool_content` | TXPool | ✔ | |
| `txpool_inspect` | TXPool | ✔ | | | `txpool_inspect` | TXPool | ✔ | |
| `txpool_status` | TXPool | ✔ | | | `txpool_status` | TXPool | ✔ | |

View File

@ -10,6 +10,7 @@ import (
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/debug" "github.com/tharsis/ethermint/ethereum/rpc/namespaces/debug"
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/eth" "github.com/tharsis/ethermint/ethereum/rpc/namespaces/eth"
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/eth/filters" "github.com/tharsis/ethermint/ethereum/rpc/namespaces/eth/filters"
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/miner"
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/net" "github.com/tharsis/ethermint/ethereum/rpc/namespaces/net"
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/personal" "github.com/tharsis/ethermint/ethereum/rpc/namespaces/personal"
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/txpool" "github.com/tharsis/ethermint/ethereum/rpc/namespaces/txpool"
@ -27,6 +28,7 @@ const (
NetNamespace = "net" NetNamespace = "net"
TxPoolNamespace = "txpool" TxPoolNamespace = "txpool"
DebugNamespace = "debug" DebugNamespace = "debug"
MinerNamespace = "miner"
apiVersion = "1.0" apiVersion = "1.0"
) )
@ -104,6 +106,15 @@ func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpccl
Public: true, Public: true,
}, },
) )
case MinerNamespace:
apis = append(apis,
rpc.API{
Namespace: MinerNamespace,
Version: apiVersion,
Service: miner.NewMinerAPI(ctx, ethAPI, evmBackend),
Public: true,
},
)
default: default:
ctx.Logger.Error("invalid namespace value", "namespace", selectedAPIs[index]) ctx.Logger.Error("invalid namespace value", "namespace", selectedAPIs[index])
} }

View File

@ -45,6 +45,8 @@ type Backend interface {
// Used by log filter // Used by log filter
GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error)
BloomStatus() (uint64, uint64) BloomStatus() (uint64, uint64)
GetCoinbase() (sdk.AccAddress, error)
} }
var _ Backend = (*EVMBackend)(nil) var _ Backend = (*EVMBackend)(nil)
@ -417,3 +419,28 @@ func (e *EVMBackend) GetLogsByNumber(blockNum types.BlockNumber) ([][]*ethtypes.
func (e *EVMBackend) BloomStatus() (uint64, uint64) { func (e *EVMBackend) BloomStatus() (uint64, uint64) {
return 4096, 0 return 4096, 0
} }
// GetCoinbase is the address that staking rewards will be send to (alias for Etherbase).
func (e *EVMBackend) GetCoinbase() (sdk.AccAddress, error) {
node, err := e.clientCtx.GetNode()
if err != nil {
return nil, err
}
status, err := node.Status(e.ctx)
if err != nil {
return nil, err
}
req := &evmtypes.QueryValidatorAccountRequest{
ConsAddress: sdk.ConsAddress(status.ValidatorInfo.Address).String(),
}
res, err := e.queryClient.ValidatorAccount(e.ctx, req)
if err != nil {
return nil, err
}
address, _ := sdk.AccAddressFromBech32(res.AccountAddress)
return address, nil
}

View File

@ -98,6 +98,14 @@ func (e *PublicAPI) ClientCtx() client.Context {
return e.clientCtx return e.clientCtx
} }
func (e *PublicAPI) QueryClient() *rpctypes.QueryClient {
return e.queryClient
}
func (e *PublicAPI) Ctx() context.Context {
return e.ctx
}
// ProtocolVersion returns the supported Ethereum protocol version. // ProtocolVersion returns the supported Ethereum protocol version.
func (e *PublicAPI) ProtocolVersion() hexutil.Uint { func (e *PublicAPI) ProtocolVersion() hexutil.Uint {
e.logger.Debug("eth_protocolVersion") e.logger.Debug("eth_protocolVersion")
@ -137,27 +145,11 @@ func (e *PublicAPI) Syncing() (interface{}, error) {
func (e *PublicAPI) Coinbase() (string, error) { func (e *PublicAPI) Coinbase() (string, error) {
e.logger.Debug("eth_coinbase") e.logger.Debug("eth_coinbase")
node, err := e.clientCtx.GetNode() coinbase, err := e.backend.GetCoinbase()
if err != nil { if err != nil {
return "", err return "", err
} }
ethAddr := common.BytesToAddress(coinbase.Bytes())
status, err := node.Status(e.ctx)
if err != nil {
return "", err
}
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 return ethAddr.Hex(), nil
} }

View File

@ -0,0 +1,191 @@
package miner
import (
"errors"
"math/big"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tharsis/ethermint/ethereum/rpc/backend"
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/eth"
rpctypes "github.com/tharsis/ethermint/ethereum/rpc/types"
)
// API is the miner prefixed set of APIs in the Miner JSON-RPC spec.
type API struct {
ctx *server.Context
logger log.Logger
ethAPI *eth.PublicAPI
backend backend.Backend
}
// NewMinerAPI creates an instance of the Miner API.
func NewMinerAPI(
ctx *server.Context,
ethAPI *eth.PublicAPI,
backend backend.Backend,
) *API {
return &API{
ctx: ctx,
ethAPI: ethAPI,
logger: ctx.Logger.With("api", "miner"),
backend: backend,
}
}
// SetEtherbase sets the etherbase of the miner
func (api *API) SetEtherbase(etherbase common.Address) bool {
api.logger.Debug("miner_setEtherbase")
delAddr, err := api.backend.GetCoinbase()
if err != nil {
api.logger.Debug("failed to get coinbase address", "error", err.Error())
return false
}
withdrawAddr := sdk.AccAddress(etherbase.Bytes())
msg := distributiontypes.NewMsgSetWithdrawAddress(delAddr, withdrawAddr)
if err := msg.ValidateBasic(); err != nil {
api.logger.Debug("tx failed basic validation", "error", err.Error())
return false
}
// Assemble transaction from fields
builder, ok := api.ethAPI.ClientCtx().TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
if !ok {
api.logger.Debug("clientCtx.TxConfig.NewTxBuilder returns unsupported builder", "error", err.Error())
return false
}
err = builder.SetMsgs(msg)
if err != nil {
api.logger.Error("builder.SetMsgs failed", "error", err.Error())
return false
}
// Fetch minimun gas price to calculate fees using the configuration.
appConf, err := config.ParseConfig(api.ctx.Viper)
if err != nil {
api.logger.Error("failed to parse file.", "file", api.ctx.Viper.ConfigFileUsed(), "error:", err.Error())
return false
}
minGasPrices := appConf.GetMinGasPrices()
if len(minGasPrices) == 0 || minGasPrices.Empty() {
api.logger.Debug("the minimun fee is not set")
return false
}
minGasPriceValue := minGasPrices[0].Amount
denom := minGasPrices[0].Denom
delCommonAddr := common.BytesToAddress(delAddr.Bytes())
nonce, err := api.ethAPI.GetTransactionCount(delCommonAddr, rpctypes.EthPendingBlockNumber)
if err != nil {
api.logger.Debug("failed to get nonce", "error", err.Error())
return false
}
txFactory := tx.Factory{}
txFactory = txFactory.
WithChainID(api.ethAPI.ClientCtx().ChainID).
WithKeybase(api.ethAPI.ClientCtx().Keyring).
WithTxConfig(api.ethAPI.ClientCtx().TxConfig).
WithSequence(uint64(*nonce)).
WithGasAdjustment(1.11)
_, gas, err := tx.CalculateGas(api.ethAPI.ClientCtx(), txFactory, msg)
if err != nil {
api.logger.Debug("failed to calculate gas", "error", err.Error())
return false
}
txFactory = txFactory.WithGas(gas)
value := new(big.Int).SetUint64(gas * minGasPriceValue.Ceil().TruncateInt().Uint64())
fees := sdk.Coins{sdk.NewCoin(denom, sdk.NewIntFromBigInt(value))}
builder.SetFeeAmount(fees)
builder.SetGasLimit(gas)
keyInfo, err := api.ethAPI.ClientCtx().Keyring.KeyByAddress(delAddr)
if err != nil {
api.logger.Debug("failed to get the wallet address using the keyring", "error", err.Error())
return false
}
if err := tx.Sign(txFactory, keyInfo.GetName(), builder, false); err != nil {
api.logger.Debug("failed to sign tx", "error", err.Error())
return false
}
// Encode transaction by default Tx encoder
txEncoder := api.ethAPI.ClientCtx().TxConfig.TxEncoder()
txBytes, err := txEncoder(builder.GetTx())
if err != nil {
api.logger.Debug("failed to encode eth tx using default encoder", "error", err.Error())
return false
}
tmHash := common.BytesToHash(tmtypes.Tx(txBytes).Hash())
// Broadcast transaction in sync mode (default)
// NOTE: If error is encountered on the node, the broadcast will not return an error
syncCtx := api.ethAPI.ClientCtx().WithBroadcastMode(flags.BroadcastSync)
rsp, err := syncCtx.BroadcastTx(txBytes)
if err != nil || rsp.Code != 0 {
if err == nil {
err = errors.New(rsp.RawLog)
}
api.logger.Debug("failed to broadcast tx", "error", err.Error())
return false
}
api.logger.Debug("broadcasted tx to set miner withdraw address (etherbase)", "hash", tmHash.String())
return true
}
// SetGasPrice sets the minimum accepted gas price for the miner.
// NOTE: this function accepts only integers to have the same interface than go-eth
// to use float values, the gas prices must be configured using the configuration file
func (api *API) SetGasPrice(gasPrice hexutil.Big) bool {
api.logger.Info(api.ctx.Viper.ConfigFileUsed())
appConf, err := config.ParseConfig(api.ctx.Viper)
if err != nil {
api.logger.Debug("failed to parse config file", "file", api.ctx.Viper.ConfigFileUsed(), "error", err.Error())
return false
}
var unit string
minGasPrices := appConf.GetMinGasPrices()
// fetch the base denom from the sdk Config in case it's not currently defined on the node config
if len(minGasPrices) == 0 || minGasPrices.Empty() {
unit, err = sdk.GetBaseDenom()
if err != nil {
api.logger.Debug("could not get the denom of smallest unit registered", "error", err.Error())
return false
}
} else {
unit = minGasPrices[0].Denom
}
c := sdk.NewDecCoin(unit, sdk.NewIntFromBigInt(gasPrice.ToInt()))
appConf.SetMinGasPrices(sdk.DecCoins{c})
config.WriteConfigFile(api.ctx.Viper.ConfigFileUsed(), appConf)
api.logger.Info("Your configuration file was modified. Please RESTART your node.", "gas-price", c.String())
return true
}

View File

@ -0,0 +1,51 @@
package miner
import (
"errors"
"github.com/ethereum/go-ethereum/common/hexutil"
)
// GetHashrate returns the current hashrate for local CPU miner and remote miner.
// Unsupported in Ethermint
func (api *API) GetHashrate() uint64 {
api.logger.Debug("miner_getHashrate")
api.logger.Debug("Unsupported rpc function: miner_getHashrate")
return 0
}
// SetExtra sets the extra data string that is included when this miner mines a block.
// Unsupported in Ethermint
func (api *API) SetExtra(extra string) (bool, error) {
api.logger.Debug("miner_setExtra")
api.logger.Debug("Unsupported rpc function: miner_setExtra")
return false, errors.New("unsupported rpc function: miner_setExtra")
}
// SetGasLimit sets the gaslimit to target towards during mining.
// Unsupported in Ethermint
func (api *API) SetGasLimit(gasLimit hexutil.Uint64) bool {
api.logger.Debug("miner_setGasLimit")
api.logger.Debug("Unsupported rpc function: miner_setGasLimit")
return false
}
// Start starts the miner with the given number of threads. If threads is nil,
// the number of workers started is equal to the number of logical CPUs that are
// usable by this process. If mining is already running, this method adjust the
// number of threads allowed to use and updates the minimum price required by the
// transaction pool.
// Unsupported in Ethermint
func (api *API) Start(threads *int) error {
api.logger.Debug("miner_start")
api.logger.Debug("Unsupported rpc function: miner_start")
return errors.New("unsupported rpc function: miner_start")
}
// Stop terminates the miner, both at the consensus engine level as well as at
// the block creation level.
// Unsupported in Ethermint
func (api *API) Stop() {
api.logger.Debug("miner_stop")
api.logger.Debug("Unsupported rpc function: miner_stop")
}

View File

@ -86,4 +86,4 @@ if [[ $1 == "pending" ]]; then
fi fi
# Start the node (remove the --pruning=nothing flag if historical queries are not needed) # Start the node (remove the --pruning=nothing flag if historical queries are not needed)
ethermintd start --pruning=nothing $TRACE --log_level $LOGLEVEL --minimum-gas-prices=0.0001aphoton --evm-rpc.api eth,txpool,personal,net,debug,web3 ethermintd start --pruning=nothing $TRACE --log_level $LOGLEVEL --minimum-gas-prices=0.0001aphoton --evm-rpc.api eth,txpool,personal,net,debug,web3,miner