2021-08-04 09:18:22 +00:00
|
|
|
package miner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"math/big"
|
|
|
|
|
2021-08-10 10:04:16 +00:00
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
|
|
|
|
2021-08-04 09:18:22 +00:00
|
|
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client/tx"
|
|
|
|
"github.com/cosmos/cosmos-sdk/server"
|
2021-08-18 14:11:51 +00:00
|
|
|
sdkconfig "github.com/cosmos/cosmos-sdk/server/config"
|
2021-08-04 09:18:22 +00:00
|
|
|
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"
|
|
|
|
rpctypes "github.com/tharsis/ethermint/ethereum/rpc/types"
|
2021-08-18 14:11:51 +00:00
|
|
|
"github.com/tharsis/ethermint/server/config"
|
2021-08-04 09:18:22 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// API is the miner prefixed set of APIs in the Miner JSON-RPC spec.
|
|
|
|
type API struct {
|
2021-08-10 10:04:16 +00:00
|
|
|
ctx *server.Context
|
|
|
|
logger log.Logger
|
|
|
|
clientCtx client.Context
|
|
|
|
backend backend.Backend
|
2021-08-04 09:18:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewMinerAPI creates an instance of the Miner API.
|
|
|
|
func NewMinerAPI(
|
|
|
|
ctx *server.Context,
|
2021-08-10 10:04:16 +00:00
|
|
|
clientCtx client.Context,
|
2021-08-04 09:18:22 +00:00
|
|
|
backend backend.Backend,
|
|
|
|
) *API {
|
|
|
|
return &API{
|
2021-08-10 10:04:16 +00:00
|
|
|
ctx: ctx,
|
|
|
|
clientCtx: clientCtx,
|
|
|
|
logger: ctx.Logger.With("api", "miner"),
|
|
|
|
backend: backend,
|
2021-08-04 09:18:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2021-08-10 10:04:16 +00:00
|
|
|
builder, ok := api.clientCtx.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
|
2021-08-04 09:18:22 +00:00
|
|
|
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())
|
2021-08-10 10:04:16 +00:00
|
|
|
nonce, err := api.backend.GetTransactionCount(delCommonAddr, rpctypes.EthPendingBlockNumber)
|
2021-08-04 09:18:22 +00:00
|
|
|
if err != nil {
|
|
|
|
api.logger.Debug("failed to get nonce", "error", err.Error())
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
txFactory := tx.Factory{}
|
|
|
|
txFactory = txFactory.
|
2021-08-10 10:04:16 +00:00
|
|
|
WithChainID(api.clientCtx.ChainID).
|
|
|
|
WithKeybase(api.clientCtx.Keyring).
|
|
|
|
WithTxConfig(api.clientCtx.TxConfig).
|
2021-08-04 09:18:22 +00:00
|
|
|
WithSequence(uint64(*nonce)).
|
2021-08-04 16:10:08 +00:00
|
|
|
WithGasAdjustment(1.25)
|
2021-08-04 09:18:22 +00:00
|
|
|
|
2021-08-10 10:04:16 +00:00
|
|
|
_, gas, err := tx.CalculateGas(api.clientCtx, txFactory, msg)
|
2021-08-04 09:18:22 +00:00
|
|
|
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)
|
|
|
|
|
2021-08-10 10:04:16 +00:00
|
|
|
keyInfo, err := api.clientCtx.Keyring.KeyByAddress(delAddr)
|
2021-08-04 09:18:22 +00:00
|
|
|
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
|
2021-08-10 10:04:16 +00:00
|
|
|
txEncoder := api.clientCtx.TxConfig.TxEncoder()
|
2021-08-04 09:18:22 +00:00
|
|
|
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
|
2021-08-10 10:04:16 +00:00
|
|
|
syncCtx := api.clientCtx.WithBroadcastMode(flags.BroadcastSync)
|
2021-08-04 09:18:22 +00:00
|
|
|
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})
|
2021-08-18 14:11:51 +00:00
|
|
|
sdkconfig.WriteConfigFile(api.ctx.Viper.ConfigFileUsed(), appConf)
|
2021-08-04 09:18:22 +00:00
|
|
|
api.logger.Info("Your configuration file was modified. Please RESTART your node.", "gas-price", c.String())
|
|
|
|
return true
|
|
|
|
}
|