rpc: configure gas cap (#457)
* rpc: configure gas cap * c++ * rm old const * docs
This commit is contained in:
parent
1b95d06c92
commit
83c838330f
@ -71,6 +71,7 @@ the Tracer type used to collect execution traces from the EVM transaction execut
|
||||
|
||||
### Improvements
|
||||
|
||||
* (rpc) [tharsis#457](https://github.com/tharsis/ethermint/pull/457) Configure RPC gas cap through app config.
|
||||
* (evm) [tharsis#434](https://github.com/tharsis/ethermint/pull/434) Support different `Tracer` types for the EVM.
|
||||
* (deps) [tharsis#427](https://github.com/tharsis/ethermint/pull/427) Bump ibc-go to [`v1.0.0`](https://github.com/cosmos/ibc-go/releases/tag/v1.0.0)
|
||||
* (gRPC) [tharsis#239](https://github.com/tharsis/ethermint/pull/239) Query `ChainConfig` via gRPC.
|
||||
|
@ -22,7 +22,19 @@ ethermintd start --json-rpc.enable
|
||||
ethermintd start --json-rpc.api eth,txpool,personal,net,debug,web3,miner
|
||||
```
|
||||
|
||||
### CORS
|
||||
## Set a Gas Cap
|
||||
|
||||
`eth_call` and `eth_estimateGas` define a global gas cap over rpc for DoS protection. You can override the default gas cap value of 25,000,000 by passing a custom value when starting the node:
|
||||
|
||||
```bash
|
||||
# set gas cap to 85M
|
||||
ethermintd start --json-rpc.gas-cap 85000000000
|
||||
|
||||
# set gas cap to infinite (=0)
|
||||
ethermintd start --json-rpc.gas-cap 0
|
||||
```
|
||||
|
||||
## CORS
|
||||
|
||||
If accessing the RPC from a browser, CORS will need to be enabled with the appropriate domain set. Otherwise, JavaScript calls are limit by the same-origin policy and requests will fail:
|
||||
|
||||
|
14079
docs/yarn.lock
14079
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -36,7 +36,7 @@ const (
|
||||
// GetRPCAPIs returns the list of all APIs
|
||||
func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, selectedAPIs []string) []rpc.API {
|
||||
nonceLock := new(types.AddrLocker)
|
||||
evmBackend := backend.NewEVMBackend(ctx.Logger, clientCtx)
|
||||
evmBackend := backend.NewEVMBackend(ctx, ctx.Logger, clientCtx)
|
||||
|
||||
var apis []rpc.API
|
||||
// remove duplicates
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
|
||||
@ -28,6 +29,7 @@ import (
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
"github.com/tharsis/ethermint/ethereum/rpc/types"
|
||||
"github.com/tharsis/ethermint/server/config"
|
||||
ethermint "github.com/tharsis/ethermint/types"
|
||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||
)
|
||||
@ -48,6 +50,7 @@ type Backend interface {
|
||||
BloomStatus() (uint64, uint64)
|
||||
GetCoinbase() (sdk.AccAddress, error)
|
||||
EstimateGas(args evmtypes.CallArgs, blockNrOptional *types.BlockNumber) (hexutil.Uint64, error)
|
||||
RPCGasCap() uint64
|
||||
}
|
||||
|
||||
var _ Backend = (*EVMBackend)(nil)
|
||||
@ -59,20 +62,28 @@ type EVMBackend struct {
|
||||
queryClient *types.QueryClient // gRPC query client
|
||||
logger log.Logger
|
||||
chainID *big.Int
|
||||
cfg config.Config
|
||||
}
|
||||
|
||||
// NewEVMBackend creates a new EVMBackend instance
|
||||
func NewEVMBackend(logger log.Logger, clientCtx client.Context) *EVMBackend {
|
||||
func NewEVMBackend(ctx *server.Context, logger log.Logger, clientCtx client.Context) *EVMBackend {
|
||||
chainID, err := ethermint.ParseChainID(clientCtx.ChainID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
appConf, err := config.ParseConfig(ctx.Viper)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &EVMBackend{
|
||||
ctx: context.Background(),
|
||||
clientCtx: clientCtx,
|
||||
queryClient: types.NewQueryClient(clientCtx),
|
||||
logger: logger.With("module", "evm-backend"),
|
||||
chainID: chainID,
|
||||
cfg: *appConf,
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,7 +558,8 @@ func (e *EVMBackend) EstimateGas(args evmtypes.CallArgs, blockNrOptional *types.
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
req := evmtypes.EthCallRequest{Args: bz, GasCap: ethermint.DefaultRPCGasLimit}
|
||||
|
||||
req := evmtypes.EthCallRequest{Args: bz, GasCap: e.RPCGasCap()}
|
||||
|
||||
// From ContextWithHeight: if the provided height is 0,
|
||||
// it will return an empty context and the gRPC query will use
|
||||
@ -581,3 +593,8 @@ func (e *EVMBackend) GetTransactionCount(address common.Address, blockNum types.
|
||||
n := hexutil.Uint64(nonce)
|
||||
return &n, nil
|
||||
}
|
||||
|
||||
// RPCGasCap is the global gas cap for eth-call variants.
|
||||
func (e *EVMBackend) RPCGasCap() uint64 {
|
||||
return e.cfg.JSONRPC.GasCap
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/tharsis/ethermint/ethereum/rpc/types"
|
||||
ethermint "github.com/tharsis/ethermint/types"
|
||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
@ -21,10 +20,8 @@ import (
|
||||
func (e *EVMBackend) setTxDefaults(args types.SendTxArgs) (types.SendTxArgs, error) {
|
||||
|
||||
if args.GasPrice == nil {
|
||||
// TODO: Change to either:
|
||||
// - min gas price from context once available through server/daemon, or
|
||||
// - suggest a gas price based on the previous included txs
|
||||
args.GasPrice = (*hexutil.Big)(big.NewInt(ethermint.DefaultGasPrice))
|
||||
// TODO: Suggest a gas price based on the previous included txs
|
||||
args.GasPrice = (*hexutil.Big)(new(big.Int).SetUint64(e.RPCGasCap()))
|
||||
}
|
||||
|
||||
if args.Nonce == nil {
|
||||
|
@ -167,8 +167,7 @@ func (e *PublicAPI) Hashrate() hexutil.Uint64 {
|
||||
// GasPrice returns the current gas price based on Ethermint's gas price oracle.
|
||||
func (e *PublicAPI) GasPrice() *hexutil.Big {
|
||||
e.logger.Debug("eth_gasPrice")
|
||||
// TODO: use minimum value defined in config instead of default or implement oracle
|
||||
out := big.NewInt(ethermint.DefaultGasPrice)
|
||||
out := new(big.Int).SetUint64(e.backend.RPCGasCap())
|
||||
return (*hexutil.Big)(out)
|
||||
}
|
||||
|
||||
@ -438,7 +437,7 @@ func (e *PublicAPI) doCall(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req := evmtypes.EthCallRequest{Args: bz, GasCap: ethermint.DefaultRPCGasLimit}
|
||||
req := evmtypes.EthCallRequest{Args: bz, GasCap: e.backend.RPCGasCap()}
|
||||
|
||||
// From ContextWithHeight: if the provided height is 0,
|
||||
// it will return an empty context and the gRPC query will use
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"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"
|
||||
sdkconfig "github.com/cosmos/cosmos-sdk/server/config"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
@ -23,6 +23,7 @@ import (
|
||||
|
||||
"github.com/tharsis/ethermint/ethereum/rpc/backend"
|
||||
rpctypes "github.com/tharsis/ethermint/ethereum/rpc/types"
|
||||
"github.com/tharsis/ethermint/server/config"
|
||||
)
|
||||
|
||||
// API is the miner prefixed set of APIs in the Miner JSON-RPC spec.
|
||||
@ -186,7 +187,7 @@ func (api *API) SetGasPrice(gasPrice hexutil.Big) bool {
|
||||
c := sdk.NewDecCoin(unit, sdk.NewIntFromBigInt(gasPrice.ToInt()))
|
||||
|
||||
appConf.SetMinGasPrices(sdk.DecCoins{c})
|
||||
config.WriteConfigFile(api.ctx.Viper.ConfigFileUsed(), appConf)
|
||||
sdkconfig.WriteConfigFile(api.ctx.Viper.ConfigFileUsed(), appConf)
|
||||
api.logger.Info("Your configuration file was modified. Please RESTART your node.", "gas-price", c.String())
|
||||
return true
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
@ -24,6 +25,8 @@ const (
|
||||
|
||||
// DefaultEVMTracer is the default vm.Tracer type
|
||||
DefaultEVMTracer = "json"
|
||||
|
||||
DefaultGasCap uint64 = 25000000
|
||||
)
|
||||
|
||||
var (
|
||||
@ -113,6 +116,27 @@ type JSONRPCConfig struct {
|
||||
Enable bool `mapstructure:"enable"`
|
||||
// EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk)
|
||||
EnableUnsafeCORS bool `mapstructure:"enable-unsafe-cors"`
|
||||
// GasCap is the global gas cap for eth-call variants.
|
||||
GasCap uint64 `mapstructure:"gas-cap"`
|
||||
}
|
||||
|
||||
// Validate returns an error if the JSON-RPC configuration fields are invalid.
|
||||
func (c JSONRPCConfig) Validate() error {
|
||||
if c.Enable && len(c.API) == 0 {
|
||||
return errors.New("cannot enable JSON-RPC without defining any API namespace")
|
||||
}
|
||||
|
||||
// TODO: validate APIs
|
||||
seenAPIs := make(map[string]bool)
|
||||
for _, api := range c.API {
|
||||
if seenAPIs[api] {
|
||||
return fmt.Errorf("repeated API namespace '%s'", api)
|
||||
}
|
||||
|
||||
seenAPIs[api] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default
|
||||
@ -123,6 +147,7 @@ func DefaultJSONRPCConfig() *JSONRPCConfig {
|
||||
Address: DefaultJSONRPCAddress,
|
||||
WsAddress: DefaultJSONRPCWsAddress,
|
||||
EnableUnsafeCORS: false,
|
||||
GasCap: DefaultGasCap,
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,17 +175,29 @@ func GetConfig(v *viper.Viper) Config {
|
||||
Address: v.GetString("json-rpc.address"),
|
||||
WsAddress: v.GetString("json-rpc.ws-address"),
|
||||
EnableUnsafeCORS: v.GetBool("json-rpc.enable-unsafe-cors"),
|
||||
GasCap: v.GetUint64("json-rpc.gas-cap"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ParseConfig retrieves the default environment configuration for the
|
||||
// application.
|
||||
func ParseConfig(v *viper.Viper) (*Config, error) {
|
||||
conf := DefaultConfig()
|
||||
err := v.Unmarshal(conf)
|
||||
|
||||
return conf, err
|
||||
}
|
||||
|
||||
// ValidateBasic returns an error any of the application configuration fields are invalid
|
||||
func (c Config) ValidateBasic() error {
|
||||
if err := c.EVM.Validate(); err != nil {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrAppConfig, "invalid evm config value: %s", err.Error())
|
||||
}
|
||||
|
||||
// TODO: validate JSON-RPC APIs
|
||||
if err := c.JSONRPC.Validate(); err != nil {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrAppConfig, "invalid json-rpc config value: %s", err.Error())
|
||||
}
|
||||
|
||||
return c.Config.ValidateBasic()
|
||||
}
|
||||
|
@ -34,4 +34,7 @@ api = "{{range $index, $elmt := .JSONRPC.API}}{{if $index}},{{$elmt}}{{else}}{{$
|
||||
|
||||
# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk)
|
||||
enable-unsafe-cors = "{{ .JSONRPC.EnableUnsafeCORS }}"
|
||||
|
||||
# GasCap sets a cap on gas that can be used in eth_call/estimateGas (0=infinite). Default: 25,000,000.
|
||||
gas-cap = {{ .JSONRPC.GasCap }}
|
||||
`
|
||||
|
@ -31,6 +31,7 @@ const (
|
||||
JSONRPCAddress = "json-rpc.address"
|
||||
JSONWsAddress = "json-rpc.ws-address"
|
||||
JSONEnableUnsafeCORS = "json-rpc.enable-unsafe-cors"
|
||||
JSONRPCGasCap = "json-rpc.gas-cap"
|
||||
)
|
||||
|
||||
// EVM flags
|
||||
|
@ -143,6 +143,7 @@ which accepts a path for the resulting pprof file.
|
||||
cmd.Flags().String(srvflags.JSONRPCAddress, config.DefaultJSONRPCAddress, "the JSON-RPC server address to listen on")
|
||||
cmd.Flags().String(srvflags.JSONWsAddress, config.DefaultJSONRPCWsAddress, "the JSON-RPC WS server address to listen on")
|
||||
cmd.Flags().Bool(srvflags.JSONEnableUnsafeCORS, false, "Define if the JSON-RPC server should enabled CORS (unsafe - use it at your own risk)")
|
||||
cmd.Flags().Uint64(srvflags.JSONRPCGasCap, config.DefaultGasCap, "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)")
|
||||
|
||||
cmd.Flags().String(srvflags.EVMTracer, config.DefaultEVMTracer, "the EVM tracer type to collect execution traces from the EVM transaction execution (json|struct|access_list|markdown)")
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
package types
|
||||
|
||||
const (
|
||||
// DefaultGasPrice is default gas price for evm transactions
|
||||
DefaultGasPrice = 20
|
||||
// DefaultRPCGasLimit is default gas limit for RPC call operations
|
||||
DefaultRPCGasLimit = 10000000
|
||||
)
|
@ -724,7 +724,7 @@ func (suite *KeeperTestSuite) deployTestContract(owner common.Address, supply *b
|
||||
|
||||
res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{
|
||||
Args: args,
|
||||
GasCap: uint64(ethermint.DefaultRPCGasLimit),
|
||||
GasCap: 25_000_000,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -805,7 +805,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
suite.SetupTest()
|
||||
gasCap = ethermint.DefaultRPCGasLimit
|
||||
gasCap = 25_000_000
|
||||
tc.malleate()
|
||||
|
||||
args, err := json.Marshal(&args)
|
||||
|
Loading…
Reference in New Issue
Block a user