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:
parent
9bf3659718
commit
396db9f20a
@ -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) [#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) [#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
|
||||
|
||||
|
@ -157,12 +157,13 @@ ethermintd start --evm-rpc.api eth,txpool,personal,net,debug,web3
|
||||
| `les_latestCheckpoint` | Les | | |
|
||||
| `les_getCheckpoint` | Les | | |
|
||||
| `les_getCheckpointContractAddress` | Les | | |
|
||||
| `miner_getHashrate` | Miner | | |
|
||||
| `miner_setExtra` | Miner | | |
|
||||
| `miner_setGasPrice` | Miner | | |
|
||||
| `miner_start` | Miner | | |
|
||||
| `miner_stop` | Miner | | |
|
||||
| `miner_setEtherbase` | Miner | | |
|
||||
| `miner_getHashrate` | Miner | N/A | Not relevant to Ethermint |
|
||||
| `miner_setExtra` | Miner | N/A | Not relevant to Ethermint |
|
||||
| `miner_setGasPrice` | Miner | ✔ | |
|
||||
| `miner_start` | Miner | N/A | Not relevant to Ethermint |
|
||||
| `miner_stop` | Miner | N/A | Not relevant to Ethermint |
|
||||
| `miner_setGasLimit` | Miner | N/A | Not relevant to Ethermint |
|
||||
| `miner_setEtherbase` | Miner | ✔ | |
|
||||
| `txpool_content` | TXPool | ✔ | |
|
||||
| `txpool_inspect` | TXPool | ✔ | |
|
||||
| `txpool_status` | TXPool | ✔ | |
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/debug"
|
||||
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/eth"
|
||||
"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/personal"
|
||||
"github.com/tharsis/ethermint/ethereum/rpc/namespaces/txpool"
|
||||
@ -27,6 +28,7 @@ const (
|
||||
NetNamespace = "net"
|
||||
TxPoolNamespace = "txpool"
|
||||
DebugNamespace = "debug"
|
||||
MinerNamespace = "miner"
|
||||
|
||||
apiVersion = "1.0"
|
||||
)
|
||||
@ -104,6 +106,15 @@ func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpccl
|
||||
Public: true,
|
||||
},
|
||||
)
|
||||
case MinerNamespace:
|
||||
apis = append(apis,
|
||||
rpc.API{
|
||||
Namespace: MinerNamespace,
|
||||
Version: apiVersion,
|
||||
Service: miner.NewMinerAPI(ctx, ethAPI, evmBackend),
|
||||
Public: true,
|
||||
},
|
||||
)
|
||||
default:
|
||||
ctx.Logger.Error("invalid namespace value", "namespace", selectedAPIs[index])
|
||||
}
|
||||
|
@ -45,6 +45,8 @@ type Backend interface {
|
||||
// Used by log filter
|
||||
GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error)
|
||||
BloomStatus() (uint64, uint64)
|
||||
|
||||
GetCoinbase() (sdk.AccAddress, error)
|
||||
}
|
||||
|
||||
var _ Backend = (*EVMBackend)(nil)
|
||||
@ -417,3 +419,28 @@ func (e *EVMBackend) GetLogsByNumber(blockNum types.BlockNumber) ([][]*ethtypes.
|
||||
func (e *EVMBackend) BloomStatus() (uint64, uint64) {
|
||||
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
|
||||
}
|
||||
|
@ -98,6 +98,14 @@ func (e *PublicAPI) ClientCtx() client.Context {
|
||||
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.
|
||||
func (e *PublicAPI) ProtocolVersion() hexutil.Uint {
|
||||
e.logger.Debug("eth_protocolVersion")
|
||||
@ -137,27 +145,11 @@ func (e *PublicAPI) Syncing() (interface{}, error) {
|
||||
func (e *PublicAPI) Coinbase() (string, error) {
|
||||
e.logger.Debug("eth_coinbase")
|
||||
|
||||
node, err := e.clientCtx.GetNode()
|
||||
coinbase, err := e.backend.GetCoinbase()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
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())
|
||||
ethAddr := common.BytesToAddress(coinbase.Bytes())
|
||||
return ethAddr.Hex(), nil
|
||||
}
|
||||
|
||||
|
191
ethereum/rpc/namespaces/miner/api.go
Normal file
191
ethereum/rpc/namespaces/miner/api.go
Normal 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
|
||||
}
|
51
ethereum/rpc/namespaces/miner/unsupported.go
Normal file
51
ethereum/rpc/namespaces/miner/unsupported.go
Normal 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")
|
||||
}
|
2
init.sh
2
init.sh
@ -86,4 +86,4 @@ if [[ $1 == "pending" ]]; then
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
Loading…
Reference in New Issue
Block a user