@@ -11,8 +11,11 @@ import (
11
11
"github.com/libp2p/go-libp2p/core/peer"
12
12
"github.com/pkg/errors"
13
13
"github.com/prometheus/client_golang/prometheus"
14
+ "go.uber.org/zap"
15
+ "golang.org/x/time/rate"
14
16
"google.golang.org/protobuf/proto"
15
17
18
+ "github.com/iotexproject/go-pkgs/cache"
16
19
"github.com/iotexproject/go-pkgs/hash"
17
20
"github.com/iotexproject/iotex-election/committee"
18
21
"github.com/iotexproject/iotex-proto/golang/iotexrpc"
@@ -79,6 +82,8 @@ type ChainService struct {
79
82
apiStats * nodestats.APILocalStats
80
83
blockTimeCalculator * blockutil.BlockTimeCalculator
81
84
actionsync * actsync.ActionSync
85
+ rateLimiters cache.LRUCache
86
+ accRateLimitCfg int
82
87
}
83
88
84
89
// Start starts the server
@@ -102,6 +107,17 @@ func (cs *ChainService) HandleAction(ctx context.Context, actPb *iotextypes.Acti
102
107
if err != nil {
103
108
return err
104
109
}
110
+ if cs .accRateLimitCfg > 0 {
111
+ sender := ""
112
+ if act .SenderAddress () != nil {
113
+ sender = act .SenderAddress ().String ()
114
+ }
115
+ limiter := cs .getRateLimiter (sender )
116
+ if ! limiter .Allow () {
117
+ log .L ().Debug ("rate limit exceeded" , zap .String ("sender" , act .SenderAddress ().String ()))
118
+ return nil
119
+ }
120
+ }
105
121
ctx = protocol .WithRegistry (ctx , cs .registry )
106
122
err = cs .actpool .Add (ctx , act )
107
123
if err != nil {
@@ -251,3 +267,12 @@ func (cs *ChainService) NewAPIServer(cfg api.Config, archive bool) (*api.ServerV
251
267
252
268
return svr , nil
253
269
}
270
+
271
+ func (cs * ChainService ) getRateLimiter (sender string ) * rate.Limiter {
272
+ limiter , exists := cs .rateLimiters .Get (sender )
273
+ if ! exists {
274
+ limiter = rate .NewLimiter (rate .Limit (cs .accRateLimitCfg ), 2 * cs .accRateLimitCfg ) // account limit request per second with a burst of *2
275
+ cs .rateLimiters .Add (sender , limiter )
276
+ }
277
+ return limiter .(* rate.Limiter )
278
+ }
0 commit comments