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

Commit

Permalink
rpc: configure gas cap (#457)
Browse files Browse the repository at this point in the history
* rpc: configure gas cap

* c++

* rm old const

* docs
  • Loading branch information
fedekunze authored Aug 18, 2021
1 parent 1b95d06 commit 83c8383
Show file tree
Hide file tree
Showing 14 changed files with 7,947 additions and 7,929 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
14 changes: 13 additions & 1 deletion docs/api/json-rpc/running_server.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
15,765 changes: 7,861 additions & 7,904 deletions docs/yarn.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion ethereum/rpc/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 19 additions & 2 deletions ethereum/rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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"
)
Expand All @@ -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)
Expand All @@ -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,
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
7 changes: 2 additions & 5 deletions ethereum/rpc/backend/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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 {
Expand Down
5 changes: 2 additions & 3 deletions ethereum/rpc/namespaces/eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions ethereum/rpc/namespaces/miner/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand Down Expand Up @@ -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
}
39 changes: 38 additions & 1 deletion server/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"errors"
"fmt"

"github.com/spf13/viper"
Expand All @@ -24,6 +25,8 @@ const (

// DefaultEVMTracer is the default vm.Tracer type
DefaultEVMTracer = "json"

DefaultGasCap uint64 = 25000000
)

var (
Expand Down Expand Up @@ -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
Expand All @@ -123,6 +147,7 @@ func DefaultJSONRPCConfig() *JSONRPCConfig {
Address: DefaultJSONRPCAddress,
WsAddress: DefaultJSONRPCWsAddress,
EnableUnsafeCORS: false,
GasCap: DefaultGasCap,
}
}

Expand Down Expand Up @@ -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()
}
3 changes: 3 additions & 0 deletions server/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
`
1 change: 1 addition & 0 deletions server/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)")

Expand Down
8 changes: 0 additions & 8 deletions types/params.go

This file was deleted.

4 changes: 2 additions & 2 deletions x/evm/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 83c8383

Please sign in to comment.