From 9e93063e16465b27cc48bacf1b96b1b2874ddb96 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Fri, 5 Apr 2019 11:52:59 +0200 Subject: [PATCH] eth, les, geth: implement cli-configurable global gas cap for RPC calls --- cmd/geth/main.go | 7 ++++--- cmd/geth/usage.go | 1 + cmd/utils/flags.go | 7 +++++++ eth/api_backend.go | 4 ++++ eth/config.go | 3 +++ internal/ethapi/api.go | 27 ++++++++++++++++++++++++--- internal/ethapi/backend.go | 1 + les/api_backend.go | 4 ++++ 8 files changed, 48 insertions(+), 6 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 1963a1a7f8..8912110227 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -57,7 +57,6 @@ var ( utils.IdentityFlag, utils.UnlockedAccountFlag, utils.PasswordFileFlag, - utils.InsecureUnlockAllowedFlag, utils.BootnodesFlag, utils.BootnodesV4Flag, utils.BootnodesV5Flag, @@ -136,8 +135,6 @@ var ( utils.VMEnableDebugFlag, utils.NetworkIdFlag, utils.ConstantinopleOverrideFlag, - utils.RPCCORSDomainFlag, - utils.RPCVirtualHostsFlag, utils.EthStatsURLFlag, utils.FakePoWFlag, utils.NoCompactionFlag, @@ -152,6 +149,8 @@ var ( utils.RPCEnabledFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, + utils.RPCCORSDomainFlag, + utils.RPCVirtualHostsFlag, utils.GraphQLEnabledFlag, utils.GraphQLListenAddrFlag, utils.GraphQLPortFlag, @@ -165,6 +164,8 @@ var ( utils.WSAllowedOriginsFlag, utils.IPCDisabledFlag, utils.IPCPathFlag, + utils.InsecureUnlockAllowedFlag, + utils.RPCGlobalGasCap, } whisperFlags = []cli.Flag{ diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 6d039ba040..7ec1ab03f5 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -158,6 +158,7 @@ var AppHelpFlagGroups = []flagGroup{ utils.RPCListenAddrFlag, utils.RPCPortFlag, utils.RPCApiFlag, + utils.RPCGlobalGasCap, utils.WSEnabledFlag, utils.WSListenAddrFlag, utils.WSPortFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f6e4288692..f5f4cde5b4 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -448,6 +448,10 @@ var ( Name: "allow-insecure-unlock", 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 EthStatsURLFlag = cli.StringFlag{ Name: "ethstats", @@ -1400,6 +1404,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { if ctx.GlobalIsSet(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. switch { diff --git a/eth/api_backend.go b/eth/api_backend.go index 29ce19e28f..00424caed2 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -218,6 +218,10 @@ func (b *EthAPIBackend) ExtRPCEnabled() bool { return b.extRPCEnabled } +func (b *EthAPIBackend) RPCGasCap() *big.Int { + return b.eth.config.RPCGasCap +} + func (b *EthAPIBackend) BloomStatus() (uint64, uint64) { sections, _, _ := b.eth.bloomIndexer.Sections() return params.BloomBitsBlocks, sections diff --git a/eth/config.go b/eth/config.go index a98e69053a..d97ae3070d 100644 --- a/eth/config.go +++ b/eth/config.go @@ -151,6 +151,9 @@ type Config struct { // Constantinople block override (TODO: remove after the fork) ConstantinopleOverride *big.Int + + // RPCGasCap is the global gas cap for eth-call variants. + RPCGasCap *big.Int `toml:",omitempty"` } type configMarshaling struct { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index e5a8124b1f..1031ef78a5 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -752,8 +752,22 @@ 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. // 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) { - result, _, _, err := DoCall(ctx, s.b, args, blockNr, vm.Config{}, 5*time.Second) - return (hexutil.Bytes)(result), err + if gasCap := s.b.RPCGasCap(); gasCap != nil { + if new(big.Int).SetUint64(uint64(*args.Gas)).Cmp(gasCap) > 0 { + log.Warn("Applying cap on gas, caller requested amount above limit", "cap", gasCap, "requested", args.Gas) + newGas := hexutil.Uint64(gasCap.Uint64()) + *args.Gas = newGas + } + result, _, _, err := DoCall(ctx, s.b, args, blockNr, vm.Config{}, 5*time.Second) + if err != nil { + err = fmt.Errorf("%v (gas capped at: %d)", err, *args.Gas) + } + return (hexutil.Bytes)(result), err + } else { + result, _, _, err := DoCall(ctx, s.b, args, blockNr, vm.Config{}, 5*time.Second) + return (hexutil.Bytes)(result), err + } + } func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Uint64, error) { @@ -797,7 +811,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 if hi == cap { 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 @@ -806,6 +820,13 @@ 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 // given transaction against the current pending block. func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) { + if gasCap := s.b.RPCGasCap(); gasCap != nil { + if new(big.Int).SetUint64(uint64(*args.Gas)).Cmp(gasCap) > 0 { + log.Warn("Applying cap on gas, caller requested amount above limit", "cap", gasCap, "requested", args.Gas) + newGas := hexutil.Uint64(gasCap.Uint64()) + *args.Gas = newGas + } + } return DoEstimateGas(ctx, s.b, args, rpc.PendingBlockNumber) } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index e88207f87d..0c6c7eace8 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -45,6 +45,7 @@ type Backend interface { EventMux() *event.TypeMux AccountManager() *accounts.Manager ExtRPCEnabled() bool + RPCGasCap() *big.Int // global gas cap for eth_call over rpc: DoS protection // BlockChain API SetHead(number uint64) diff --git a/les/api_backend.go b/les/api_backend.go index 8b03979a29..4fe3521366 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -192,6 +192,10 @@ func (b *LesApiBackend) ExtRPCEnabled() bool { return b.extRPCEnabled } +func (b *LesApiBackend) RPCGasCap() *big.Int { + return b.eth.config.RPCGasCap +} + func (b *LesApiBackend) BloomStatus() (uint64, uint64) { if b.eth.bloomIndexer == nil { return 0, 0