Skip to content

Commit

Permalink
Enforce maximum response size for 'getLogs' requests (#222)
Browse files Browse the repository at this point in the history
  • Loading branch information
wanliqun authored Sep 14, 2024
1 parent e749b3f commit 2da55bb
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 0 deletions.
4 changes: 4 additions & 0 deletions config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,10 @@ node:
# maxSplitEpochRange: 1000
# # Maximum block range for the log filter split to the full node
# maxSplitBlockRange: 1000
# # RPC handler
# rpc:
# # Maximum number of bytes for the response body of getLogs requests
# maxGetLogsResponseBytes: 10 * 1024 * 1024

# # Go performance profiling
# pprof:
Expand Down
51 changes: 51 additions & 0 deletions rpc/handler/cfx_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,32 @@ import (
"github.com/Conflux-Chain/confura/util/metrics"
sdk "github.com/Conflux-Chain/go-conflux-sdk"
"github.com/Conflux-Chain/go-conflux-sdk/types"
"github.com/Conflux-Chain/go-conflux-util/viper"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
)

var (
// Maximum number of bytes for the response body of getLogs requests
maxGetLogsResponseBytes uint64
errResponseBodySizeTooLarge error

errEventLogsTooStale = errors.New("event logs are too stale (already pruned)")
)

func Init() {
var constraint struct {
MaxGetLogsResponseBytes uint64 `default:"10485760"` // default 10MB
}
viper.MustUnmarshalKey("constraints.rpc", &constraint)

maxGetLogsResponseBytes = constraint.MaxGetLogsResponseBytes
errResponseBodySizeTooLarge = fmt.Errorf(
"result body size is too large with more than %d bytes, please narrow down your filter condition",
constraint.MaxGetLogsResponseBytes,
)
}

// CfxLogsApiHandler RPC handler to get core space event logs from store or fullnode.
type CfxLogsApiHandler struct {
ms *mysql.MysqlStore
Expand Down Expand Up @@ -98,6 +116,7 @@ func (handler *CfxLogsApiHandler) getLogsReorgGuard(
}

var logs []types.Log
var bodySizeAccumulator responseBodySizeAccumulator

// query data from database
for i := range dbFilters {
Expand All @@ -110,6 +129,10 @@ func (handler *CfxLogsApiHandler) getLogsReorgGuard(
// succeeded to get logs from database
if err == nil {
for _, v := range dbLogs {
if err := bodySizeAccumulator.Add(len(v.Extra)); err != nil {
return nil, false, err
}

log, _ := v.ToCfxLog()
logs = append(logs, *log)
}
Expand Down Expand Up @@ -144,6 +167,11 @@ func (handler *CfxLogsApiHandler) getLogsReorgGuard(
return nil, false, err
}

for i := range fnLogs {
if err := bodySizeAccumulator.Add(len(fnLogs[i].Data)); err != nil {
return nil, false, err
}
}
logs = append(logs, fnLogs...)
}

Expand All @@ -164,6 +192,11 @@ func (handler *CfxLogsApiHandler) getLogsReorgGuard(
return nil, false, err
}

for i := range fnLogs {
if err := bodySizeAccumulator.Add(len(fnLogs[i].Data)); err != nil {
return nil, false, err
}
}
logs = append(logs, fnLogs...)
}

Expand Down Expand Up @@ -427,3 +460,21 @@ func calculateEpochRange(filter *types.LogFilter) (int64, bool) {

return epochTo - epochFrom + 1, true
}

// responseBodySizeAccumulator is a helper to check if the result body size exceeds the limit.
type responseBodySizeAccumulator struct {
accumulator uint64
}

// Add adds the given size to the accumulator and checks if the result body size exceeds the limit.
func (rb *responseBodySizeAccumulator) Add(size int) error {
// Add the new size to the accumulator.
rb.accumulator += uint64(size)

// If the accumulator exceeds the limit, return an error.
if rb.accumulator > maxGetLogsResponseBytes {
return errResponseBodySizeTooLarge
}

return nil
}
10 changes: 10 additions & 0 deletions rpc/handler/eth_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func (handler *EthLogsApiHandler) getLogsReorgGuard(
}

var logs []types.Log
var bodySizeAccumulator responseBodySizeAccumulator

// query data from database
if dbFilter != nil {
Expand All @@ -98,6 +99,10 @@ func (handler *EthLogsApiHandler) getLogsReorgGuard(
}

for _, v := range dbLogs {
if err := bodySizeAccumulator.Add(len(v.Extra)); err != nil {
return nil, false, err
}

cfxLog, ext := v.ToCfxLog()
logs = append(logs, *ethbridge.ConvertLog(cfxLog, ext))
}
Expand All @@ -120,6 +125,11 @@ func (handler *EthLogsApiHandler) getLogsReorgGuard(
return nil, false, err
}

for i := range fnLogs {
if err := bodySizeAccumulator.Add(len(fnLogs[i].Data)); err != nil {
return nil, false, err
}
}
logs = append(logs, fnLogs...)
}

Expand Down
4 changes: 4 additions & 0 deletions rpc/server_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"

"github.com/Conflux-Chain/confura/node"
"github.com/Conflux-Chain/confura/rpc/handler"
"github.com/Conflux-Chain/confura/util/rate"
"github.com/Conflux-Chain/confura/util/rpc/handlers"
"github.com/Conflux-Chain/confura/util/rpc/middlewares"
Expand All @@ -19,6 +20,9 @@ const (
)

func MustInit() {
// init handler
handler.Init()

// init metrics
initMetrics()

Expand Down

0 comments on commit 2da55bb

Please sign in to comment.