forked from cerc-io/plugeth
eth, les, geth: implement cli-configurable global gas cap for RPC calls (#19401)
* eth, les, geth: implement cli-configurable global gas cap for RPC calls * graphql, ethapi: place gas cap in DoCall * ethapi: reformat log message
This commit is contained in:
parent
ed97517ff4
commit
e2f3465e83
@ -57,7 +57,6 @@ var (
|
|||||||
utils.IdentityFlag,
|
utils.IdentityFlag,
|
||||||
utils.UnlockedAccountFlag,
|
utils.UnlockedAccountFlag,
|
||||||
utils.PasswordFileFlag,
|
utils.PasswordFileFlag,
|
||||||
utils.InsecureUnlockAllowedFlag,
|
|
||||||
utils.BootnodesFlag,
|
utils.BootnodesFlag,
|
||||||
utils.BootnodesV4Flag,
|
utils.BootnodesV4Flag,
|
||||||
utils.BootnodesV5Flag,
|
utils.BootnodesV5Flag,
|
||||||
@ -136,8 +135,6 @@ var (
|
|||||||
utils.VMEnableDebugFlag,
|
utils.VMEnableDebugFlag,
|
||||||
utils.NetworkIdFlag,
|
utils.NetworkIdFlag,
|
||||||
utils.ConstantinopleOverrideFlag,
|
utils.ConstantinopleOverrideFlag,
|
||||||
utils.RPCCORSDomainFlag,
|
|
||||||
utils.RPCVirtualHostsFlag,
|
|
||||||
utils.EthStatsURLFlag,
|
utils.EthStatsURLFlag,
|
||||||
utils.FakePoWFlag,
|
utils.FakePoWFlag,
|
||||||
utils.NoCompactionFlag,
|
utils.NoCompactionFlag,
|
||||||
@ -152,6 +149,8 @@ var (
|
|||||||
utils.RPCEnabledFlag,
|
utils.RPCEnabledFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.RPCListenAddrFlag,
|
||||||
utils.RPCPortFlag,
|
utils.RPCPortFlag,
|
||||||
|
utils.RPCCORSDomainFlag,
|
||||||
|
utils.RPCVirtualHostsFlag,
|
||||||
utils.GraphQLEnabledFlag,
|
utils.GraphQLEnabledFlag,
|
||||||
utils.GraphQLListenAddrFlag,
|
utils.GraphQLListenAddrFlag,
|
||||||
utils.GraphQLPortFlag,
|
utils.GraphQLPortFlag,
|
||||||
@ -165,6 +164,8 @@ var (
|
|||||||
utils.WSAllowedOriginsFlag,
|
utils.WSAllowedOriginsFlag,
|
||||||
utils.IPCDisabledFlag,
|
utils.IPCDisabledFlag,
|
||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
|
utils.InsecureUnlockAllowedFlag,
|
||||||
|
utils.RPCGlobalGasCap,
|
||||||
}
|
}
|
||||||
|
|
||||||
whisperFlags = []cli.Flag{
|
whisperFlags = []cli.Flag{
|
||||||
|
@ -158,6 +158,7 @@ var AppHelpFlagGroups = []flagGroup{
|
|||||||
utils.RPCListenAddrFlag,
|
utils.RPCListenAddrFlag,
|
||||||
utils.RPCPortFlag,
|
utils.RPCPortFlag,
|
||||||
utils.RPCApiFlag,
|
utils.RPCApiFlag,
|
||||||
|
utils.RPCGlobalGasCap,
|
||||||
utils.WSEnabledFlag,
|
utils.WSEnabledFlag,
|
||||||
utils.WSListenAddrFlag,
|
utils.WSListenAddrFlag,
|
||||||
utils.WSPortFlag,
|
utils.WSPortFlag,
|
||||||
|
@ -448,6 +448,10 @@ var (
|
|||||||
Name: "allow-insecure-unlock",
|
Name: "allow-insecure-unlock",
|
||||||
Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http",
|
Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http",
|
||||||
}
|
}
|
||||||
|
RPCGlobalGasCap = cli.Uint64Flag{
|
||||||
|
Name: "rpc.gascap",
|
||||||
|
Usage: "Sets a cap on gas that can be used in eth_call/estimateGas",
|
||||||
|
}
|
||||||
// Logging and debug settings
|
// Logging and debug settings
|
||||||
EthStatsURLFlag = cli.StringFlag{
|
EthStatsURLFlag = cli.StringFlag{
|
||||||
Name: "ethstats",
|
Name: "ethstats",
|
||||||
@ -1400,6 +1404,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
|||||||
if ctx.GlobalIsSet(EVMInterpreterFlag.Name) {
|
if ctx.GlobalIsSet(EVMInterpreterFlag.Name) {
|
||||||
cfg.EVMInterpreter = ctx.GlobalString(EVMInterpreterFlag.Name)
|
cfg.EVMInterpreter = ctx.GlobalString(EVMInterpreterFlag.Name)
|
||||||
}
|
}
|
||||||
|
if ctx.GlobalIsSet(RPCGlobalGasCap.Name) {
|
||||||
|
cfg.RPCGasCap = new(big.Int).SetUint64(ctx.GlobalUint64(RPCGlobalGasCap.Name))
|
||||||
|
}
|
||||||
|
|
||||||
// Override any default configs for hard coded networks.
|
// Override any default configs for hard coded networks.
|
||||||
switch {
|
switch {
|
||||||
|
@ -218,6 +218,10 @@ func (b *EthAPIBackend) ExtRPCEnabled() bool {
|
|||||||
return b.extRPCEnabled
|
return b.extRPCEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthAPIBackend) RPCGasCap() *big.Int {
|
||||||
|
return b.eth.config.RPCGasCap
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) BloomStatus() (uint64, uint64) {
|
func (b *EthAPIBackend) BloomStatus() (uint64, uint64) {
|
||||||
sections, _, _ := b.eth.bloomIndexer.Sections()
|
sections, _, _ := b.eth.bloomIndexer.Sections()
|
||||||
return params.BloomBitsBlocks, sections
|
return params.BloomBitsBlocks, sections
|
||||||
|
@ -151,6 +151,9 @@ type Config struct {
|
|||||||
|
|
||||||
// Constantinople block override (TODO: remove after the fork)
|
// Constantinople block override (TODO: remove after the fork)
|
||||||
ConstantinopleOverride *big.Int
|
ConstantinopleOverride *big.Int
|
||||||
|
|
||||||
|
// RPCGasCap is the global gas cap for eth-call variants.
|
||||||
|
RPCGasCap *big.Int `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type configMarshaling struct {
|
type configMarshaling struct {
|
||||||
|
@ -856,7 +856,7 @@ func (b *Block) Call(ctx context.Context, args struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result, gas, failed, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.num, vm.Config{}, 5*time.Second)
|
result, gas, failed, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.num, vm.Config{}, 5*time.Second, b.backend.RPCGasCap())
|
||||||
status := hexutil.Uint64(1)
|
status := hexutil.Uint64(1)
|
||||||
if failed {
|
if failed {
|
||||||
status = 0
|
status = 0
|
||||||
@ -883,7 +883,7 @@ func (b *Block) EstimateGas(ctx context.Context, args struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gas, err := ethapi.DoEstimateGas(ctx, b.backend, args.Data, *b.num)
|
gas, err := ethapi.DoEstimateGas(ctx, b.backend, args.Data, *b.num, b.backend.RPCGasCap())
|
||||||
return gas, err
|
return gas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -927,7 +927,7 @@ func (p *Pending) Account(ctx context.Context, args struct {
|
|||||||
func (p *Pending) Call(ctx context.Context, args struct {
|
func (p *Pending) Call(ctx context.Context, args struct {
|
||||||
Data ethapi.CallArgs
|
Data ethapi.CallArgs
|
||||||
}) (*CallResult, error) {
|
}) (*CallResult, error) {
|
||||||
result, gas, failed, err := ethapi.DoCall(ctx, p.backend, args.Data, rpc.PendingBlockNumber, vm.Config{}, 5*time.Second)
|
result, gas, failed, err := ethapi.DoCall(ctx, p.backend, args.Data, rpc.PendingBlockNumber, vm.Config{}, 5*time.Second, p.backend.RPCGasCap())
|
||||||
status := hexutil.Uint64(1)
|
status := hexutil.Uint64(1)
|
||||||
if failed {
|
if failed {
|
||||||
status = 0
|
status = 0
|
||||||
@ -942,7 +942,7 @@ func (p *Pending) Call(ctx context.Context, args struct {
|
|||||||
func (p *Pending) EstimateGas(ctx context.Context, args struct {
|
func (p *Pending) EstimateGas(ctx context.Context, args struct {
|
||||||
Data ethapi.CallArgs
|
Data ethapi.CallArgs
|
||||||
}) (hexutil.Uint64, error) {
|
}) (hexutil.Uint64, error) {
|
||||||
return ethapi.DoEstimateGas(ctx, p.backend, args.Data, rpc.PendingBlockNumber)
|
return ethapi.DoEstimateGas(ctx, p.backend, args.Data, rpc.PendingBlockNumber, p.backend.RPCGasCap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolver is the top-level object in the GraphQL hierarchy.
|
// Resolver is the top-level object in the GraphQL hierarchy.
|
||||||
|
@ -674,7 +674,7 @@ type CallArgs struct {
|
|||||||
Data *hexutil.Bytes `json:"data"`
|
Data *hexutil.Bytes `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error) {
|
func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) {
|
||||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
state, header, err := b.StateAndHeaderByNumber(ctx, blockNr)
|
state, header, err := b.StateAndHeaderByNumber(ctx, blockNr)
|
||||||
@ -697,6 +697,10 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumb
|
|||||||
if args.Gas != nil {
|
if args.Gas != nil {
|
||||||
gas = uint64(*args.Gas)
|
gas = uint64(*args.Gas)
|
||||||
}
|
}
|
||||||
|
if globalGasCap != nil && globalGasCap.Uint64() < gas {
|
||||||
|
log.Warn("Caller gas above allowance, capping", "requested", gas, "cap", globalGasCap)
|
||||||
|
gas = globalGasCap.Uint64()
|
||||||
|
}
|
||||||
gasPrice := new(big.Int).SetUint64(defaultGasPrice)
|
gasPrice := new(big.Int).SetUint64(defaultGasPrice)
|
||||||
if args.GasPrice != nil {
|
if args.GasPrice != nil {
|
||||||
gasPrice = args.GasPrice.ToInt()
|
gasPrice = args.GasPrice.ToInt()
|
||||||
@ -752,11 +756,11 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumb
|
|||||||
// Call executes the given transaction on the state for the given block number.
|
// Call executes the given transaction on the state for the given block number.
|
||||||
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
||||||
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||||
result, _, _, err := DoCall(ctx, s.b, args, blockNr, vm.Config{}, 5*time.Second)
|
result, _, _, err := DoCall(ctx, s.b, args, blockNr, vm.Config{}, 5*time.Second, s.b.RPCGasCap())
|
||||||
return (hexutil.Bytes)(result), err
|
return (hexutil.Bytes)(result), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Uint64, error) {
|
func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, gasCap *big.Int) (hexutil.Uint64, error) {
|
||||||
// Binary search the gas requirement, as it may be higher than the amount used
|
// Binary search the gas requirement, as it may be higher than the amount used
|
||||||
var (
|
var (
|
||||||
lo uint64 = params.TxGas - 1
|
lo uint64 = params.TxGas - 1
|
||||||
@ -773,13 +777,17 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl
|
|||||||
}
|
}
|
||||||
hi = block.GasLimit()
|
hi = block.GasLimit()
|
||||||
}
|
}
|
||||||
|
if gasCap != nil && hi > gasCap.Uint64() {
|
||||||
|
log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
|
||||||
|
hi = gasCap.Uint64()
|
||||||
|
}
|
||||||
cap = hi
|
cap = hi
|
||||||
|
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
// Create a helper to check if a gas allowance results in an executable transaction
|
||||||
executable := func(gas uint64) bool {
|
executable := func(gas uint64) bool {
|
||||||
args.Gas = (*hexutil.Uint64)(&gas)
|
args.Gas = (*hexutil.Uint64)(&gas)
|
||||||
|
|
||||||
_, _, failed, err := DoCall(ctx, b, args, rpc.PendingBlockNumber, vm.Config{}, 0)
|
_, _, failed, err := DoCall(ctx, b, args, rpc.PendingBlockNumber, vm.Config{}, 0, gasCap)
|
||||||
if err != nil || failed {
|
if err != nil || failed {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -797,7 +805,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl
|
|||||||
// Reject the transaction as invalid if it still fails at the highest allowance
|
// Reject the transaction as invalid if it still fails at the highest allowance
|
||||||
if hi == cap {
|
if hi == cap {
|
||||||
if !executable(hi) {
|
if !executable(hi) {
|
||||||
return 0, fmt.Errorf("gas required exceeds allowance or always failing transaction")
|
return 0, fmt.Errorf("gas required exceeds allowance (%d) or always failing transaction", cap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hexutil.Uint64(hi), nil
|
return hexutil.Uint64(hi), nil
|
||||||
@ -806,7 +814,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl
|
|||||||
// EstimateGas returns an estimate of the amount of gas needed to execute the
|
// EstimateGas returns an estimate of the amount of gas needed to execute the
|
||||||
// given transaction against the current pending block.
|
// given transaction against the current pending block.
|
||||||
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
|
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
|
||||||
return DoEstimateGas(ctx, s.b, args, rpc.PendingBlockNumber)
|
return DoEstimateGas(ctx, s.b, args, rpc.PendingBlockNumber, s.b.RPCGasCap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecutionResult groups all structured logs emitted by the EVM
|
// ExecutionResult groups all structured logs emitted by the EVM
|
||||||
|
@ -45,6 +45,7 @@ type Backend interface {
|
|||||||
EventMux() *event.TypeMux
|
EventMux() *event.TypeMux
|
||||||
AccountManager() *accounts.Manager
|
AccountManager() *accounts.Manager
|
||||||
ExtRPCEnabled() bool
|
ExtRPCEnabled() bool
|
||||||
|
RPCGasCap() *big.Int // global gas cap for eth_call over rpc: DoS protection
|
||||||
|
|
||||||
// BlockChain API
|
// BlockChain API
|
||||||
SetHead(number uint64)
|
SetHead(number uint64)
|
||||||
|
@ -192,6 +192,10 @@ func (b *LesApiBackend) ExtRPCEnabled() bool {
|
|||||||
return b.extRPCEnabled
|
return b.extRPCEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *LesApiBackend) RPCGasCap() *big.Int {
|
||||||
|
return b.eth.config.RPCGasCap
|
||||||
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) BloomStatus() (uint64, uint64) {
|
func (b *LesApiBackend) BloomStatus() (uint64, uint64) {
|
||||||
if b.eth.bloomIndexer == nil {
|
if b.eth.bloomIndexer == nil {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
|
Loading…
Reference in New Issue
Block a user