Skip to content

Commit a2f7947

Browse files
committed
Add transaction rate-limit functionality for testnet
1 parent 6f4a6cb commit a2f7947

File tree

4 files changed

+38
-1
lines changed

4 files changed

+38
-1
lines changed

api/utils.go

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

cmd/run/cmd.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,12 @@ func parseConfigFromFlags() error {
222222
return fmt.Errorf("unknown tx state validation: %s", txStateValidation)
223223
}
224224

225+
txDuration, err := time.ParseDuration(txDurationLimit)
226+
if err != nil {
227+
return fmt.Errorf("invalid unit %s for TX duration limit: %w", txDuration, err)
228+
}
229+
cfg.TxDurationLimit = txDuration
230+
225231
return nil
226232
}
227233

@@ -242,7 +248,8 @@ var (
242248
cloudKMSLocationID,
243249
cloudKMSKeyRingID,
244250
walletKey,
245-
txStateValidation string
251+
txStateValidation,
252+
txDurationLimit string
246253

247254
initHeight,
248255
forceStartHeight uint64
@@ -280,4 +287,6 @@ func init() {
280287
Cmd.Flags().StringVar(&cfg.ProfilerHost, "profiler-host", "localhost", "Host for the Profiler server")
281288
Cmd.Flags().IntVar(&cfg.ProfilerPort, "profiler-port", 6060, "Port for the Profiler server")
282289
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'.")
290+
Cmd.Flags().Uint64Var(&cfg.TxRequestLimit, "tx-request-limit", 1, "Number of transaction submissions to allow per the specified interval. Applies only on Testnet.")
291+
Cmd.Flags().StringVar(&txDurationLimit, "tx-duration-limit", "3s", "Time interval upon which to enforce transaction submission rate limiting. Applies only on Testnet.")
283292
}

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+
// TxDurationLimit is the time interval upon which to enforce transaction submission
95+
// rate limiting.
96+
TxDurationLimit time.Duration
9297
}

services/requester/requester.go

Lines changed: 21 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"
@@ -110,6 +112,7 @@ type EVM struct {
110112
evmSigner types.Signer
111113
validationOptions *txpool.ValidationOptions
112114
collector metrics.Collector
115+
limiter limiter.Store
113116
}
114117

115118
func NewEVM(
@@ -166,6 +169,16 @@ func NewEVM(
166169
MinTip: new(big.Int),
167170
}
168171

172+
ratelimiter, err := memorystore.New(
173+
&memorystore.Config{
174+
Tokens: config.TxRequestLimit,
175+
Interval: config.TxDurationLimit,
176+
},
177+
)
178+
if err != nil {
179+
return nil, fmt.Errorf("failed to create TX rate limiter: %w", err)
180+
}
181+
169182
evm := &EVM{
170183
registerStore: registerStore,
171184
client: client,
@@ -178,6 +191,7 @@ func NewEVM(
178191
validationOptions: validationOptions,
179192
collector: collector,
180193
keystore: keystore,
194+
limiter: ratelimiter,
181195
}
182196

183197
return evm, nil
@@ -202,6 +216,13 @@ func (e *EVM) SendRawTransaction(ctx context.Context, data []byte) (common.Hash,
202216
return common.Hash{}, fmt.Errorf("failed to derive the sender: %w", err)
203217
}
204218

219+
if e.config.FlowNetworkID == flowGo.Testnet {
220+
_, _, _, ok, _ := e.limiter.Take(ctx, from.Hex())
221+
if !ok {
222+
return common.Hash{}, errs.ErrRateLimit
223+
}
224+
}
225+
205226
if tx.GasPrice().Cmp(e.config.GasPrice) < 0 {
206227
return common.Hash{}, errs.NewTxGasPriceTooLowError(e.config.GasPrice)
207228
}

0 commit comments

Comments
 (0)