Skip to content

Commit

Permalink
[p2p] introduce account rate limit (#4545)
Browse files Browse the repository at this point in the history
  • Loading branch information
envestcc authored and dustinxie committed Jan 31, 2025
1 parent ca44110 commit 32f02de
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 0 deletions.
8 changes: 8 additions & 0 deletions chainservice/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/url"
"time"

"github.com/iotexproject/go-pkgs/cache"
"github.com/iotexproject/iotex-address/address"
"github.com/iotexproject/iotex-election/committee"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
Expand Down Expand Up @@ -102,6 +103,13 @@ func (builder *Builder) SetP2PAgent(agent p2p.Agent) *Builder {
return builder
}

func (builder *Builder) SetAccountRateLimit(r int) *Builder {
builder.createInstance()
builder.cs.accRateLimitCfg = r
builder.cs.rateLimiters = cache.NewThreadSafeLruCache(10000)
return builder
}

// SetRPCStats sets the RPCStats instance
func (builder *Builder) SetRPCStats(stats *nodestats.APILocalStats) *Builder {
builder.createInstance()
Expand Down
25 changes: 25 additions & 0 deletions chainservice/chainservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import (
"github.com/libp2p/go-libp2p/core/peer"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
"golang.org/x/time/rate"
"google.golang.org/protobuf/proto"

"github.com/iotexproject/go-pkgs/cache"
"github.com/iotexproject/go-pkgs/hash"
"github.com/iotexproject/iotex-election/committee"
"github.com/iotexproject/iotex-proto/golang/iotexrpc"
Expand Down Expand Up @@ -79,6 +82,8 @@ type ChainService struct {
apiStats *nodestats.APILocalStats
blockTimeCalculator *blockutil.BlockTimeCalculator
actionsync *actsync.ActionSync
rateLimiters cache.LRUCache
accRateLimitCfg int
}

// Start starts the server
Expand All @@ -102,6 +107,17 @@ func (cs *ChainService) HandleAction(ctx context.Context, actPb *iotextypes.Acti
if err != nil {
return err
}
if cs.accRateLimitCfg > 0 {
sender := ""
if act.SenderAddress() != nil {
sender = act.SenderAddress().String()
}
limiter := cs.getRateLimiter(sender)
if !limiter.Allow() {
log.L().Debug("rate limit exceeded", zap.String("sender", act.SenderAddress().String()))
return nil
}
}
ctx = protocol.WithRegistry(ctx, cs.registry)
err = cs.actpool.Add(ctx, act)
if err != nil {
Expand Down Expand Up @@ -248,3 +264,12 @@ func (cs *ChainService) NewAPIServer(cfg api.Config, plugins map[int]interface{}

return svr, nil
}

func (cs *ChainService) getRateLimiter(sender string) *rate.Limiter {
limiter, exists := cs.rateLimiters.Get(sender)
if !exists {
limiter = rate.NewLimiter(rate.Limit(cs.accRateLimitCfg), 2*cs.accRateLimitCfg) // account limit request per second with a burst of *2
cs.rateLimiters.Add(sender, limiter)
}
return limiter.(*rate.Limiter)
}
1 change: 1 addition & 0 deletions e2etest/local_actpool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ func newActPoolConfig(t *testing.T) (config.Config, error) {
cfg.ActPool.MinGasPriceStr = "0"
cfg.Consensus.Scheme = config.NOOPScheme
cfg.Network.Port = testutil.RandomPort()
cfg.Network.AccountRateLimit = 0

sk, err := crypto.GenerateKey()
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions p2p/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ type (
PrivateNetworkPSK string `yaml:"privateNetworkPSK"`
MaxPeers int `yaml:"maxPeers"`
MaxMessageSize int `yaml:"maxMessageSize"`
// AccountRateLimit is the maximum number of requests per second per account.
AccountRateLimit int `yaml:"accountRateLimit"`
}

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

// NewDummyAgent creates a dummy p2p agent
Expand Down
1 change: 1 addition & 0 deletions server/itx/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func newServer(cfg config.Config, testing bool) (*Server, error) {
var cs *chainservice.ChainService
builder := chainservice.NewBuilder(cfg)
builder.SetP2PAgent(p2pAgent)
builder.SetAccountRateLimit(cfg.Network.AccountRateLimit)
rpcStats := nodestats.NewAPILocalStats()
builder.SetRPCStats(rpcStats)
if testing {
Expand Down

0 comments on commit 32f02de

Please sign in to comment.