Skip to content

Commit a99b8ab

Browse files
authored
[p2p] introduce account rate limit (#4545)
1 parent e43c527 commit a99b8ab

File tree

5 files changed

+38
-0
lines changed

5 files changed

+38
-0
lines changed

chainservice/builder.go

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"net/url"
1212
"time"
1313

14+
"github.com/iotexproject/go-pkgs/cache"
1415
"github.com/iotexproject/iotex-address/address"
1516
"github.com/iotexproject/iotex-election/committee"
1617
"github.com/iotexproject/iotex-proto/golang/iotextypes"
@@ -102,6 +103,13 @@ func (builder *Builder) SetP2PAgent(agent p2p.Agent) *Builder {
102103
return builder
103104
}
104105

106+
func (builder *Builder) SetAccountRateLimit(r int) *Builder {
107+
builder.createInstance()
108+
builder.cs.accRateLimitCfg = r
109+
builder.cs.rateLimiters = cache.NewThreadSafeLruCache(10000)
110+
return builder
111+
}
112+
105113
// SetRPCStats sets the RPCStats instance
106114
func (builder *Builder) SetRPCStats(stats *nodestats.APILocalStats) *Builder {
107115
builder.createInstance()

chainservice/chainservice.go

+25
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ import (
1111
"github.com/libp2p/go-libp2p/core/peer"
1212
"github.com/pkg/errors"
1313
"github.com/prometheus/client_golang/prometheus"
14+
"go.uber.org/zap"
15+
"golang.org/x/time/rate"
1416
"google.golang.org/protobuf/proto"
1517

18+
"github.com/iotexproject/go-pkgs/cache"
1619
"github.com/iotexproject/go-pkgs/hash"
1720
"github.com/iotexproject/iotex-election/committee"
1821
"github.com/iotexproject/iotex-proto/golang/iotexrpc"
@@ -79,6 +82,8 @@ type ChainService struct {
7982
apiStats *nodestats.APILocalStats
8083
blockTimeCalculator *blockutil.BlockTimeCalculator
8184
actionsync *actsync.ActionSync
85+
rateLimiters cache.LRUCache
86+
accRateLimitCfg int
8287
}
8388

8489
// Start starts the server
@@ -102,6 +107,17 @@ func (cs *ChainService) HandleAction(ctx context.Context, actPb *iotextypes.Acti
102107
if err != nil {
103108
return err
104109
}
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+
}
105121
ctx = protocol.WithRegistry(ctx, cs.registry)
106122
err = cs.actpool.Add(ctx, act)
107123
if err != nil {
@@ -251,3 +267,12 @@ func (cs *ChainService) NewAPIServer(cfg api.Config, archive bool) (*api.ServerV
251267

252268
return svr, nil
253269
}
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+
}

e2etest/local_actpool_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ func newActPoolConfig(t *testing.T) (config.Config, error) {
183183
cfg.ActPool.MinGasPriceStr = "0"
184184
cfg.Consensus.Scheme = config.NOOPScheme
185185
cfg.Network.Port = testutil.RandomPort()
186+
cfg.Network.AccountRateLimit = 0
186187

187188
sk, err := crypto.GenerateKey()
188189
if err != nil {

p2p/agent.go

+3
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ type (
9595
PrivateNetworkPSK string `yaml:"privateNetworkPSK"`
9696
MaxPeers int `yaml:"maxPeers"`
9797
MaxMessageSize int `yaml:"maxMessageSize"`
98+
// AccountRateLimit is the maximum number of requests per second per account.
99+
AccountRateLimit int `yaml:"accountRateLimit"`
98100
}
99101

100102
// Agent is the agent to help the blockchain node connect into the P2P networks and send/receive messages
@@ -145,6 +147,7 @@ var DefaultConfig = Config{
145147
PrivateNetworkPSK: "",
146148
MaxPeers: 30,
147149
MaxMessageSize: p2p.DefaultConfig.MaxMessageSize,
150+
AccountRateLimit: 100,
148151
}
149152

150153
// NewDummyAgent creates a dummy p2p agent

server/itx/server.go

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func newServer(cfg config.Config, testing bool) (*Server, error) {
7373
var cs *chainservice.ChainService
7474
builder := chainservice.NewBuilder(cfg)
7575
builder.SetP2PAgent(p2pAgent)
76+
builder.SetAccountRateLimit(cfg.Network.AccountRateLimit)
7677
rpcStats := nodestats.NewAPILocalStats()
7778
builder.SetRPCStats(rpcStats)
7879
if testing {

0 commit comments

Comments
 (0)