Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ func handleError[T any](err error, log zerolog.Logger, collector metrics.Collect
return zero, err
case errors.Is(err, core.ErrInsufficientFunds):
return zero, err
case errors.Is(err, errs.ErrRateLimit):
return zero, err
default:
collector.ApiErrorOccurred()
log.Error().Err(err).Msg("api error")
Expand Down
2 changes: 2 additions & 0 deletions cmd/run/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,6 @@ func init() {
Cmd.Flags().StringVar(&cfg.ProfilerHost, "profiler-host", "localhost", "Host for the Profiler server")
Cmd.Flags().IntVar(&cfg.ProfilerPort, "profiler-port", 6060, "Port for the Profiler server")
Cmd.Flags().StringVar(&txStateValidation, "tx-state-validation", "tx-seal", "Sets the transaction validation mechanism. It can validate using the local state index, or wait for the outer Flow transaction to seal. Available values ('local-index' / 'tx-seal'), defaults to 'tx-seal'.")
Cmd.Flags().Uint64Var(&cfg.TxRequestLimit, "tx-request-limit", 0, "Number of transaction submissions to allow per the specified interval.")
Cmd.Flags().DurationVar(&cfg.TxRequestLimitDuration, "tx-request-limit-duration", time.Second*3, "Time interval upon which to enforce transaction submission rate limiting.")
}
5 changes: 5 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,9 @@ type Config struct {
// TxStateValidation sets the transaction validation mechanism. It can validate
// using the local state index, or wait for the outer Flow transaction to seal.
TxStateValidation string
// TxRequestLimit is the number of transaction submissions to allow per interval.
TxRequestLimit uint64
// TxRequestLimitDuration is the time interval upon which to enforce transaction submission
// rate limiting.
TxRequestLimitDuration time.Duration
}
25 changes: 25 additions & 0 deletions services/requester/requester.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"github.com/onflow/go-ethereum/core/txpool"
"github.com/onflow/go-ethereum/core/types"
"github.com/rs/zerolog"
"github.com/sethvargo/go-limiter"
"github.com/sethvargo/go-limiter/memorystore"
"golang.org/x/sync/errgroup"

"github.com/onflow/flow-evm-gateway/config"
Expand Down Expand Up @@ -109,6 +111,7 @@ type EVM struct {
evmSigner types.Signer
validationOptions *txpool.ValidationOptions
collector metrics.Collector
rateLimiter limiter.Store
}

func NewEVM(
Expand Down Expand Up @@ -165,6 +168,16 @@ func NewEVM(
MinTip: new(big.Int),
}

rateLimiter, err := memorystore.New(
&memorystore.Config{
Tokens: config.TxRequestLimit,
Interval: config.TxRequestLimitDuration,
},
)
if err != nil {
return nil, fmt.Errorf("failed to create TX rate limiter: %w", err)
}

evm := &EVM{
registerStore: registerStore,
client: client,
Expand All @@ -177,6 +190,7 @@ func NewEVM(
validationOptions: validationOptions,
collector: collector,
keystore: keystore,
rateLimiter: rateLimiter,
}

return evm, nil
Expand All @@ -197,6 +211,17 @@ func (e *EVM) SendRawTransaction(ctx context.Context, data []byte) (common.Hash,
return common.Hash{}, fmt.Errorf("failed to derive the sender: %w", err)
}

if e.config.TxRequestLimit > 0 {
_, _, _, ok, err := e.rateLimiter.Take(ctx, from.Hex())
if err != nil {
return common.Hash{}, fmt.Errorf("failed to check rate limit: %w", err)
}
if !ok {
e.collector.RequestRateLimited("SendRawTransaction")
return common.Hash{}, errs.ErrRateLimit
}
}

if tx.GasPrice().Cmp(e.config.GasPrice) < 0 {
return common.Hash{}, errs.NewTxGasPriceTooLowError(e.config.GasPrice)
}
Expand Down
Loading