Skip to content

Commit acc0b78

Browse files
authored
Merge pull request #750 from onflow/mpeter/testnet-tx-submission-rate-limit
Add transaction rate-limit functionality
2 parents 35d2260 + 2f462fa commit acc0b78

File tree

4 files changed

+34
-0
lines changed

4 files changed

+34
-0
lines changed

api/utils.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ func handleError[T any](err error, log zerolog.Logger, collector metrics.Collect
132132
return zero, err
133133
case errors.Is(err, core.ErrInsufficientFunds):
134134
return zero, err
135+
case errors.Is(err, errs.ErrRateLimit):
136+
return zero, err
135137
default:
136138
collector.ApiErrorOccurred()
137139
log.Error().Err(err).Msg("api error")

cmd/run/cmd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,6 @@ func init() {
280280
Cmd.Flags().StringVar(&cfg.ProfilerHost, "profiler-host", "localhost", "Host for the Profiler server")
281281
Cmd.Flags().IntVar(&cfg.ProfilerPort, "profiler-port", 6060, "Port for the Profiler server")
282282
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'.")
283+
Cmd.Flags().Uint64Var(&cfg.TxRequestLimit, "tx-request-limit", 0, "Number of transaction submissions to allow per the specified interval.")
284+
Cmd.Flags().DurationVar(&cfg.TxRequestLimitDuration, "tx-request-limit-duration", time.Second*3, "Time interval upon which to enforce transaction submission rate limiting.")
283285
}

config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,9 @@ type Config struct {
8989
// TxStateValidation sets the transaction validation mechanism. It can validate
9090
// using the local state index, or wait for the outer Flow transaction to seal.
9191
TxStateValidation string
92+
// TxRequestLimit is the number of transaction submissions to allow per interval.
93+
TxRequestLimit uint64
94+
// TxRequestLimitDuration is the time interval upon which to enforce transaction submission
95+
// rate limiting.
96+
TxRequestLimitDuration time.Duration
9297
}

services/requester/requester.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"github.com/onflow/go-ethereum/core/txpool"
2222
"github.com/onflow/go-ethereum/core/types"
2323
"github.com/rs/zerolog"
24+
"github.com/sethvargo/go-limiter"
25+
"github.com/sethvargo/go-limiter/memorystore"
2426
"golang.org/x/sync/errgroup"
2527

2628
"github.com/onflow/flow-evm-gateway/config"
@@ -109,6 +111,7 @@ type EVM struct {
109111
evmSigner types.Signer
110112
validationOptions *txpool.ValidationOptions
111113
collector metrics.Collector
114+
rateLimiter limiter.Store
112115
}
113116

114117
func NewEVM(
@@ -165,6 +168,16 @@ func NewEVM(
165168
MinTip: new(big.Int),
166169
}
167170

171+
rateLimiter, err := memorystore.New(
172+
&memorystore.Config{
173+
Tokens: config.TxRequestLimit,
174+
Interval: config.TxRequestLimitDuration,
175+
},
176+
)
177+
if err != nil {
178+
return nil, fmt.Errorf("failed to create TX rate limiter: %w", err)
179+
}
180+
168181
evm := &EVM{
169182
registerStore: registerStore,
170183
client: client,
@@ -177,6 +190,7 @@ func NewEVM(
177190
validationOptions: validationOptions,
178191
collector: collector,
179192
keystore: keystore,
193+
rateLimiter: rateLimiter,
180194
}
181195

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

214+
if e.config.TxRequestLimit > 0 {
215+
_, _, _, ok, err := e.rateLimiter.Take(ctx, from.Hex())
216+
if err != nil {
217+
return common.Hash{}, fmt.Errorf("failed to check rate limit: %w", err)
218+
}
219+
if !ok {
220+
e.collector.RequestRateLimited("SendRawTransaction")
221+
return common.Hash{}, errs.ErrRateLimit
222+
}
223+
}
224+
200225
if tx.GasPrice().Cmp(e.config.GasPrice) < 0 {
201226
return common.Hash{}, errs.NewTxGasPriceTooLowError(e.config.GasPrice)
202227
}

0 commit comments

Comments
 (0)