forked from cerc-io/laconicd-deprecated
imp(rpc): restrict unprotected txs on the node RPC (#1143)
* imp(rpc): restrict unprotected txs on the node RPC * lint * send raw transaction * c++
This commit is contained in:
parent
8817e8052a
commit
3b852f723e
@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||||||
|
|
||||||
### API Breaking
|
### API Breaking
|
||||||
|
|
||||||
|
* (rpc) [\#1143](https://github.com/evmos/ethermint/pull/1143) Restrict unprotected txs on the node JSON-RPC configuration.
|
||||||
* (all) [\#1137](https://github.com/evmos/ethermint/pull/1137) Rename go module to `evmos/ethermint`
|
* (all) [\#1137](https://github.com/evmos/ethermint/pull/1137) Rename go module to `evmos/ethermint`
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
33
rpc/apis.go
33
rpc/apis.go
@ -44,16 +44,21 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// APICreator creates the JSON-RPC API implementations.
|
// APICreator creates the JSON-RPC API implementations.
|
||||||
type APICreator = func(*server.Context, client.Context, *rpcclient.WSClient) []rpc.API
|
type APICreator = func(
|
||||||
|
ctx *server.Context,
|
||||||
|
clientCtx client.Context,
|
||||||
|
tendermintWebsocketClient *rpcclient.WSClient,
|
||||||
|
allowUnprotectedTxs bool,
|
||||||
|
) []rpc.API
|
||||||
|
|
||||||
// apiCreators defines the JSON-RPC API namespaces.
|
// apiCreators defines the JSON-RPC API namespaces.
|
||||||
var apiCreators map[string]APICreator
|
var apiCreators map[string]APICreator
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
apiCreators = map[string]APICreator{
|
apiCreators = map[string]APICreator{
|
||||||
EthNamespace: func(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient) []rpc.API {
|
EthNamespace: func(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, allowUnprotectedTxs bool) []rpc.API {
|
||||||
nonceLock := new(types.AddrLocker)
|
nonceLock := new(types.AddrLocker)
|
||||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx)
|
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs)
|
||||||
return []rpc.API{
|
return []rpc.API{
|
||||||
{
|
{
|
||||||
Namespace: EthNamespace,
|
Namespace: EthNamespace,
|
||||||
@ -69,7 +74,7 @@ func init() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Web3Namespace: func(*server.Context, client.Context, *rpcclient.WSClient) []rpc.API {
|
Web3Namespace: func(*server.Context, client.Context, *rpcclient.WSClient, bool) []rpc.API {
|
||||||
return []rpc.API{
|
return []rpc.API{
|
||||||
{
|
{
|
||||||
Namespace: Web3Namespace,
|
Namespace: Web3Namespace,
|
||||||
@ -79,7 +84,7 @@ func init() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NetNamespace: func(_ *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API {
|
NetNamespace: func(_ *server.Context, clientCtx client.Context, _ *rpcclient.WSClient, _ bool) []rpc.API {
|
||||||
return []rpc.API{
|
return []rpc.API{
|
||||||
{
|
{
|
||||||
Namespace: NetNamespace,
|
Namespace: NetNamespace,
|
||||||
@ -89,8 +94,8 @@ func init() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PersonalNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API {
|
PersonalNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient, allowUnprotectedTxs bool) []rpc.API {
|
||||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx)
|
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs)
|
||||||
return []rpc.API{
|
return []rpc.API{
|
||||||
{
|
{
|
||||||
Namespace: PersonalNamespace,
|
Namespace: PersonalNamespace,
|
||||||
@ -100,7 +105,7 @@ func init() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TxPoolNamespace: func(ctx *server.Context, _ client.Context, _ *rpcclient.WSClient) []rpc.API {
|
TxPoolNamespace: func(ctx *server.Context, _ client.Context, _ *rpcclient.WSClient, _ bool) []rpc.API {
|
||||||
return []rpc.API{
|
return []rpc.API{
|
||||||
{
|
{
|
||||||
Namespace: TxPoolNamespace,
|
Namespace: TxPoolNamespace,
|
||||||
@ -110,8 +115,8 @@ func init() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DebugNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API {
|
DebugNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient, allowUnprotectedTxs bool) []rpc.API {
|
||||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx)
|
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs)
|
||||||
return []rpc.API{
|
return []rpc.API{
|
||||||
{
|
{
|
||||||
Namespace: DebugNamespace,
|
Namespace: DebugNamespace,
|
||||||
@ -121,8 +126,8 @@ func init() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MinerNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API {
|
MinerNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient, allowUnprotectedTxs bool) []rpc.API {
|
||||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx)
|
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs)
|
||||||
return []rpc.API{
|
return []rpc.API{
|
||||||
{
|
{
|
||||||
Namespace: MinerNamespace,
|
Namespace: MinerNamespace,
|
||||||
@ -136,12 +141,12 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRPCAPIs returns the list of all APIs
|
// GetRPCAPIs returns the list of all APIs
|
||||||
func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, selectedAPIs []string) []rpc.API {
|
func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, allowUnprotectedTxs bool, selectedAPIs []string) []rpc.API {
|
||||||
var apis []rpc.API
|
var apis []rpc.API
|
||||||
|
|
||||||
for _, ns := range selectedAPIs {
|
for _, ns := range selectedAPIs {
|
||||||
if creator, ok := apiCreators[ns]; ok {
|
if creator, ok := apiCreators[ns]; ok {
|
||||||
apis = append(apis, creator(ctx, clientCtx, tmWSClient)...)
|
apis = append(apis, creator(ctx, clientCtx, tmWSClient, allowUnprotectedTxs)...)
|
||||||
} else {
|
} else {
|
||||||
ctx.Logger.Error("invalid namespace value", "namespace", ns)
|
ctx.Logger.Error("invalid namespace value", "namespace", ns)
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ type EVMBackend interface {
|
|||||||
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
|
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
|
||||||
RPCEVMTimeout() time.Duration // global timeout for eth_call over rpc: DoS protection
|
RPCEVMTimeout() time.Duration // global timeout for eth_call over rpc: DoS protection
|
||||||
RPCTxFeeCap() float64 // RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for send-transaction variants. The unit is ether.
|
RPCTxFeeCap() float64 // RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for send-transaction variants. The unit is ether.
|
||||||
|
UnprotectedAllowed() bool
|
||||||
|
|
||||||
RPCMinGasPrice() int64
|
RPCMinGasPrice() int64
|
||||||
SuggestGasTipCap(baseFee *big.Int) (*big.Int, error)
|
SuggestGasTipCap(baseFee *big.Int) (*big.Int, error)
|
||||||
@ -86,16 +87,17 @@ var _ BackendI = (*Backend)(nil)
|
|||||||
|
|
||||||
// Backend implements the BackendI interface
|
// Backend implements the BackendI interface
|
||||||
type Backend struct {
|
type Backend struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
clientCtx client.Context
|
clientCtx client.Context
|
||||||
queryClient *types.QueryClient // gRPC query client
|
queryClient *types.QueryClient // gRPC query client
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
chainID *big.Int
|
chainID *big.Int
|
||||||
cfg config.Config
|
cfg config.Config
|
||||||
|
allowUnprotectedTxs bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBackend creates a new Backend instance for cosmos and ethereum namespaces
|
// NewBackend creates a new Backend instance for cosmos and ethereum namespaces
|
||||||
func NewBackend(ctx *server.Context, logger log.Logger, clientCtx client.Context) *Backend {
|
func NewBackend(ctx *server.Context, logger log.Logger, clientCtx client.Context, allowUnprotectedTxs bool) *Backend {
|
||||||
chainID, err := ethermint.ParseChainID(clientCtx.ChainID)
|
chainID, err := ethermint.ParseChainID(clientCtx.ChainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -104,11 +106,12 @@ func NewBackend(ctx *server.Context, logger log.Logger, clientCtx client.Context
|
|||||||
appConf := config.GetConfig(ctx.Viper)
|
appConf := config.GetConfig(ctx.Viper)
|
||||||
|
|
||||||
return &Backend{
|
return &Backend{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
clientCtx: clientCtx,
|
clientCtx: clientCtx,
|
||||||
queryClient: types.NewQueryClient(clientCtx),
|
queryClient: types.NewQueryClient(clientCtx),
|
||||||
logger: logger.With("module", "backend"),
|
logger: logger.With("module", "backend"),
|
||||||
chainID: chainID,
|
chainID: chainID,
|
||||||
cfg: appConf,
|
cfg: appConf,
|
||||||
|
allowUnprotectedTxs: allowUnprotectedTxs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -638,7 +638,15 @@ func (b *Backend) SendTransaction(args evmtypes.TransactionArgs) (common.Hash, e
|
|||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
txHash := msg.AsTransaction().Hash()
|
ethTx := msg.AsTransaction()
|
||||||
|
|
||||||
|
// check the local node config in case unprotected txs are disabled
|
||||||
|
if !b.UnprotectedAllowed() && !ethTx.Protected() {
|
||||||
|
// Ensure only eip155 signed transactions are submitted if EIP155Required is set.
|
||||||
|
return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC")
|
||||||
|
}
|
||||||
|
|
||||||
|
txHash := ethTx.Hash()
|
||||||
|
|
||||||
// Broadcast transaction in sync mode (default)
|
// Broadcast transaction in sync mode (default)
|
||||||
// NOTE: If error is encountered on the node, the broadcast will not return an error
|
// NOTE: If error is encountered on the node, the broadcast will not return an error
|
||||||
@ -956,3 +964,9 @@ func (b *Backend) GetEthereumMsgsFromTendermintBlock(resBlock *tmrpctypes.Result
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnprotectedAllowed returns the node configuration value for allowing
|
||||||
|
// unprotected transactions (i.e not replay-protected)
|
||||||
|
func (b Backend) UnprotectedAllowed() bool {
|
||||||
|
return b.allowUnprotectedTxs
|
||||||
|
}
|
||||||
|
@ -525,6 +525,12 @@ func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error)
|
|||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check the local node config in case unprotected txs are disabled
|
||||||
|
if !e.backend.UnprotectedAllowed() && !tx.Protected() {
|
||||||
|
// Ensure only eip155 signed transactions are submitted if EIP155Required is set.
|
||||||
|
return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC")
|
||||||
|
}
|
||||||
|
|
||||||
ethereumTx := &evmtypes.MsgEthereumTx{}
|
ethereumTx := &evmtypes.MsgEthereumTx{}
|
||||||
if err := ethereumTx.FromEthereumTx(tx); err != nil {
|
if err := ethereumTx.FromEthereumTx(tx); err != nil {
|
||||||
e.logger.Error("transaction converting failed", "error", err.Error())
|
e.logger.Error("transaction converting failed", "error", err.Error())
|
||||||
|
@ -47,6 +47,8 @@ const (
|
|||||||
DefaultHTTPTimeout = 30 * time.Second
|
DefaultHTTPTimeout = 30 * time.Second
|
||||||
|
|
||||||
DefaultHTTPIdleTimeout = 120 * time.Second
|
DefaultHTTPIdleTimeout = 120 * time.Second
|
||||||
|
// DefaultAllowUnprotectedTxs value is false
|
||||||
|
DefaultAllowUnprotectedTxs = false
|
||||||
)
|
)
|
||||||
|
|
||||||
var evmTracers = []string{"json", "markdown", "struct", "access_list"}
|
var evmTracers = []string{"json", "markdown", "struct", "access_list"}
|
||||||
@ -98,6 +100,9 @@ type JSONRPCConfig struct {
|
|||||||
HTTPTimeout time.Duration `mapstructure:"http-timeout"`
|
HTTPTimeout time.Duration `mapstructure:"http-timeout"`
|
||||||
// HTTPIdleTimeout is the idle timeout of http json-rpc server.
|
// HTTPIdleTimeout is the idle timeout of http json-rpc server.
|
||||||
HTTPIdleTimeout time.Duration `mapstructure:"http-idle-timeout"`
|
HTTPIdleTimeout time.Duration `mapstructure:"http-idle-timeout"`
|
||||||
|
// AllowUnprotectedTxs restricts unprotected (non EIP155 signed) transactions to be submitted via
|
||||||
|
// the node's RPC when global parameter is disabled.
|
||||||
|
AllowUnprotectedTxs bool `mapstructure:"allow-unprotected-txs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSConfig defines the certificate and matching private key for the server.
|
// TLSConfig defines the certificate and matching private key for the server.
|
||||||
@ -183,19 +188,20 @@ func GetAPINamespaces() []string {
|
|||||||
// DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default
|
// DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default
|
||||||
func DefaultJSONRPCConfig() *JSONRPCConfig {
|
func DefaultJSONRPCConfig() *JSONRPCConfig {
|
||||||
return &JSONRPCConfig{
|
return &JSONRPCConfig{
|
||||||
Enable: true,
|
Enable: true,
|
||||||
API: GetDefaultAPINamespaces(),
|
API: GetDefaultAPINamespaces(),
|
||||||
Address: DefaultJSONRPCAddress,
|
Address: DefaultJSONRPCAddress,
|
||||||
WsAddress: DefaultJSONRPCWsAddress,
|
WsAddress: DefaultJSONRPCWsAddress,
|
||||||
GasCap: DefaultGasCap,
|
GasCap: DefaultGasCap,
|
||||||
EVMTimeout: DefaultEVMTimeout,
|
EVMTimeout: DefaultEVMTimeout,
|
||||||
TxFeeCap: DefaultTxFeeCap,
|
TxFeeCap: DefaultTxFeeCap,
|
||||||
FilterCap: DefaultFilterCap,
|
FilterCap: DefaultFilterCap,
|
||||||
FeeHistoryCap: DefaultFeeHistoryCap,
|
FeeHistoryCap: DefaultFeeHistoryCap,
|
||||||
BlockRangeCap: DefaultBlockRangeCap,
|
BlockRangeCap: DefaultBlockRangeCap,
|
||||||
LogsCap: DefaultLogsCap,
|
LogsCap: DefaultLogsCap,
|
||||||
HTTPTimeout: DefaultHTTPTimeout,
|
HTTPTimeout: DefaultHTTPTimeout,
|
||||||
HTTPIdleTimeout: DefaultHTTPIdleTimeout,
|
HTTPIdleTimeout: DefaultHTTPIdleTimeout,
|
||||||
|
AllowUnprotectedTxs: DefaultAllowUnprotectedTxs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +62,10 @@ http-timeout = "{{ .JSONRPC.HTTPTimeout }}"
|
|||||||
# HTTPIdleTimeout is the idle timeout of http json-rpc server.
|
# HTTPIdleTimeout is the idle timeout of http json-rpc server.
|
||||||
http-idle-timeout = "{{ .JSONRPC.HTTPIdleTimeout }}"
|
http-idle-timeout = "{{ .JSONRPC.HTTPIdleTimeout }}"
|
||||||
|
|
||||||
|
# AllowUnprotectedTxs restricts unprotected (non EIP155 signed) transactions to be submitted via
|
||||||
|
# the node's RPC when the global parameter is disabled.
|
||||||
|
allow-unprotected-txs = {{ .JSONRPC.AllowUnprotectedTxs }}
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
### TLS Configuration ###
|
### TLS Configuration ###
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -32,18 +32,19 @@ const (
|
|||||||
|
|
||||||
// JSON-RPC flags
|
// JSON-RPC flags
|
||||||
const (
|
const (
|
||||||
JSONRPCEnable = "json-rpc.enable"
|
JSONRPCEnable = "json-rpc.enable"
|
||||||
JSONRPCAPI = "json-rpc.api"
|
JSONRPCAPI = "json-rpc.api"
|
||||||
JSONRPCAddress = "json-rpc.address"
|
JSONRPCAddress = "json-rpc.address"
|
||||||
JSONWsAddress = "json-rpc.ws-address"
|
JSONWsAddress = "json-rpc.ws-address"
|
||||||
JSONRPCGasCap = "json-rpc.gas-cap"
|
JSONRPCGasCap = "json-rpc.gas-cap"
|
||||||
JSONRPCEVMTimeout = "json-rpc.evm-timeout"
|
JSONRPCEVMTimeout = "json-rpc.evm-timeout"
|
||||||
JSONRPCTxFeeCap = "json-rpc.txfee-cap"
|
JSONRPCTxFeeCap = "json-rpc.txfee-cap"
|
||||||
JSONRPCFilterCap = "json-rpc.filter-cap"
|
JSONRPCFilterCap = "json-rpc.filter-cap"
|
||||||
JSONRPCLogsCap = "json-rpc.logs-cap"
|
JSONRPCLogsCap = "json-rpc.logs-cap"
|
||||||
JSONRPCBlockRangeCap = "json-rpc.block-range-cap"
|
JSONRPCBlockRangeCap = "json-rpc.block-range-cap"
|
||||||
JSONRPCHTTPTimeout = "json-rpc.http-timeout"
|
JSONRPCHTTPTimeout = "json-rpc.http-timeout"
|
||||||
JSONRPCHTTPIdleTimeout = "json-rpc.http-idle-timeout"
|
JSONRPCHTTPIdleTimeout = "json-rpc.http-idle-timeout"
|
||||||
|
JSONRPCAllowUnprotectedTxs = "json-rpc.allow-unprotected-txs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EVM flags
|
// EVM flags
|
||||||
|
@ -36,8 +36,10 @@ func StartJSONRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr, tmEn
|
|||||||
|
|
||||||
rpcServer := ethrpc.NewServer()
|
rpcServer := ethrpc.NewServer()
|
||||||
|
|
||||||
|
allowUnprotectedTxs := config.JSONRPC.AllowUnprotectedTxs
|
||||||
rpcAPIArr := config.JSONRPC.API
|
rpcAPIArr := config.JSONRPC.API
|
||||||
apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient, rpcAPIArr)
|
|
||||||
|
apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient, allowUnprotectedTxs, rpcAPIArr)
|
||||||
|
|
||||||
for _, api := range apis {
|
for _, api := range apis {
|
||||||
if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil {
|
if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil {
|
||||||
|
@ -162,6 +162,7 @@ which accepts a path for the resulting pprof file.
|
|||||||
cmd.Flags().Duration(srvflags.JSONRPCEVMTimeout, config.DefaultEVMTimeout, "Sets a timeout used for eth_call (0=infinite)")
|
cmd.Flags().Duration(srvflags.JSONRPCEVMTimeout, config.DefaultEVMTimeout, "Sets a timeout used for eth_call (0=infinite)")
|
||||||
cmd.Flags().Duration(srvflags.JSONRPCHTTPTimeout, config.DefaultHTTPTimeout, "Sets a read/write timeout for json-rpc http server (0=infinite)")
|
cmd.Flags().Duration(srvflags.JSONRPCHTTPTimeout, config.DefaultHTTPTimeout, "Sets a read/write timeout for json-rpc http server (0=infinite)")
|
||||||
cmd.Flags().Duration(srvflags.JSONRPCHTTPIdleTimeout, config.DefaultHTTPIdleTimeout, "Sets a idle timeout for json-rpc http server (0=infinite)")
|
cmd.Flags().Duration(srvflags.JSONRPCHTTPIdleTimeout, config.DefaultHTTPIdleTimeout, "Sets a idle timeout for json-rpc http server (0=infinite)")
|
||||||
|
cmd.Flags().Bool(srvflags.JSONRPCAllowUnprotectedTxs, config.DefaultAllowUnprotectedTxs, "Allow for unprotected (non EIP155 signed) transactions to be submitted via the node's RPC when the global parameter is disabled")
|
||||||
cmd.Flags().Int32(srvflags.JSONRPCLogsCap, config.DefaultLogsCap, "Sets the max number of results can be returned from single `eth_getLogs` query")
|
cmd.Flags().Int32(srvflags.JSONRPCLogsCap, config.DefaultLogsCap, "Sets the max number of results can be returned from single `eth_getLogs` query")
|
||||||
cmd.Flags().Int32(srvflags.JSONRPCBlockRangeCap, config.DefaultBlockRangeCap, "Sets the max block range allowed for `eth_getLogs` query")
|
cmd.Flags().Int32(srvflags.JSONRPCBlockRangeCap, config.DefaultBlockRangeCap, "Sets the max block range allowed for `eth_getLogs` query")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user