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
|
||||
|
||||
* (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`
|
||||
|
||||
### Improvements
|
||||
|
33
rpc/apis.go
33
rpc/apis.go
@ -44,16 +44,21 @@ const (
|
||||
)
|
||||
|
||||
// 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.
|
||||
var apiCreators map[string]APICreator
|
||||
|
||||
func init() {
|
||||
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)
|
||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx)
|
||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs)
|
||||
return []rpc.API{
|
||||
{
|
||||
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{
|
||||
{
|
||||
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{
|
||||
{
|
||||
Namespace: NetNamespace,
|
||||
@ -89,8 +94,8 @@ func init() {
|
||||
},
|
||||
}
|
||||
},
|
||||
PersonalNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API {
|
||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx)
|
||||
PersonalNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient, allowUnprotectedTxs bool) []rpc.API {
|
||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs)
|
||||
return []rpc.API{
|
||||
{
|
||||
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{
|
||||
{
|
||||
Namespace: TxPoolNamespace,
|
||||
@ -110,8 +115,8 @@ func init() {
|
||||
},
|
||||
}
|
||||
},
|
||||
DebugNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API {
|
||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx)
|
||||
DebugNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient, allowUnprotectedTxs bool) []rpc.API {
|
||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs)
|
||||
return []rpc.API{
|
||||
{
|
||||
Namespace: DebugNamespace,
|
||||
@ -121,8 +126,8 @@ func init() {
|
||||
},
|
||||
}
|
||||
},
|
||||
MinerNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient) []rpc.API {
|
||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx)
|
||||
MinerNamespace: func(ctx *server.Context, clientCtx client.Context, _ *rpcclient.WSClient, allowUnprotectedTxs bool) []rpc.API {
|
||||
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs)
|
||||
return []rpc.API{
|
||||
{
|
||||
Namespace: MinerNamespace,
|
||||
@ -136,12 +141,12 @@ func init() {
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
for _, ns := range selectedAPIs {
|
||||
if creator, ok := apiCreators[ns]; ok {
|
||||
apis = append(apis, creator(ctx, clientCtx, tmWSClient)...)
|
||||
apis = append(apis, creator(ctx, clientCtx, tmWSClient, allowUnprotectedTxs)...)
|
||||
} else {
|
||||
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
|
||||
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.
|
||||
UnprotectedAllowed() bool
|
||||
|
||||
RPCMinGasPrice() int64
|
||||
SuggestGasTipCap(baseFee *big.Int) (*big.Int, error)
|
||||
@ -92,10 +93,11 @@ type Backend struct {
|
||||
logger log.Logger
|
||||
chainID *big.Int
|
||||
cfg config.Config
|
||||
allowUnprotectedTxs bool
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -110,5 +112,6 @@ func NewBackend(ctx *server.Context, logger log.Logger, clientCtx client.Context
|
||||
logger: logger.With("module", "backend"),
|
||||
chainID: chainID,
|
||||
cfg: appConf,
|
||||
allowUnprotectedTxs: allowUnprotectedTxs,
|
||||
}
|
||||
}
|
||||
|
@ -638,7 +638,15 @@ func (b *Backend) SendTransaction(args evmtypes.TransactionArgs) (common.Hash, e
|
||||
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)
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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{}
|
||||
if err := ethereumTx.FromEthereumTx(tx); err != nil {
|
||||
e.logger.Error("transaction converting failed", "error", err.Error())
|
||||
|
@ -47,6 +47,8 @@ const (
|
||||
DefaultHTTPTimeout = 30 * time.Second
|
||||
|
||||
DefaultHTTPIdleTimeout = 120 * time.Second
|
||||
// DefaultAllowUnprotectedTxs value is false
|
||||
DefaultAllowUnprotectedTxs = false
|
||||
)
|
||||
|
||||
var evmTracers = []string{"json", "markdown", "struct", "access_list"}
|
||||
@ -98,6 +100,9 @@ type JSONRPCConfig struct {
|
||||
HTTPTimeout time.Duration `mapstructure:"http-timeout"`
|
||||
// HTTPIdleTimeout is the idle timeout of http json-rpc server.
|
||||
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.
|
||||
@ -196,6 +201,7 @@ func DefaultJSONRPCConfig() *JSONRPCConfig {
|
||||
LogsCap: DefaultLogsCap,
|
||||
HTTPTimeout: DefaultHTTPTimeout,
|
||||
HTTPIdleTimeout: DefaultHTTPIdleTimeout,
|
||||
AllowUnprotectedTxs: DefaultAllowUnprotectedTxs,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,10 @@ http-timeout = "{{ .JSONRPC.HTTPTimeout }}"
|
||||
# HTTPIdleTimeout is the idle timeout of http json-rpc server.
|
||||
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 ###
|
||||
###############################################################################
|
||||
|
@ -44,6 +44,7 @@ const (
|
||||
JSONRPCBlockRangeCap = "json-rpc.block-range-cap"
|
||||
JSONRPCHTTPTimeout = "json-rpc.http-timeout"
|
||||
JSONRPCHTTPIdleTimeout = "json-rpc.http-idle-timeout"
|
||||
JSONRPCAllowUnprotectedTxs = "json-rpc.allow-unprotected-txs"
|
||||
)
|
||||
|
||||
// EVM flags
|
||||
|
@ -36,8 +36,10 @@ func StartJSONRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr, tmEn
|
||||
|
||||
rpcServer := ethrpc.NewServer()
|
||||
|
||||
allowUnprotectedTxs := config.JSONRPC.AllowUnprotectedTxs
|
||||
rpcAPIArr := config.JSONRPC.API
|
||||
apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient, rpcAPIArr)
|
||||
|
||||
apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient, allowUnprotectedTxs, rpcAPIArr)
|
||||
|
||||
for _, api := range apis {
|
||||
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.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().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.JSONRPCBlockRangeCap, config.DefaultBlockRangeCap, "Sets the max block range allowed for `eth_getLogs` query")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user