Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jsonrpc): configurable eth_getLogs limits #2903

Merged
merged 2 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions components/webapi/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/iotaledger/wasp/packages/chains"
"github.com/iotaledger/wasp/packages/daemon"
"github.com/iotaledger/wasp/packages/dkg"
"github.com/iotaledger/wasp/packages/evm/jsonrpc"
"github.com/iotaledger/wasp/packages/isc"
"github.com/iotaledger/wasp/packages/metrics"
"github.com/iotaledger/wasp/packages/peering"
Expand Down Expand Up @@ -291,6 +292,10 @@ func provide(c *dig.Container) error {
websocketService,
ParamsWebAPI.IndexDbPath,
deps.Publisher,
jsonrpc.NewParameters(
ParamsWebAPI.Limits.Jsonrpc.MaxBlocksInLogsFilterRange,
ParamsWebAPI.Limits.Jsonrpc.MaxLogsInResult,
),
)

return webapiServerResult{
Expand Down
33 changes: 20 additions & 13 deletions components/webapi/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,29 @@ import (
)

type ParametersWebAPI struct {
Enabled bool `default:"true" usage:"whether the web api plugin is enabled"`
BindAddress string `default:"0.0.0.0:9090" usage:"the bind address for the node web api"`
Auth authentication.AuthConfiguration `usage:"configures the authentication for the API service"`
IndexDbPath string `default:"waspdb/chains/index" usage:"directory for storing indexes of historical data (only archive nodes will create/use them)"`
Limits struct {
Timeout time.Duration `default:"30s" usage:"the timeout after which a long running operation will be canceled"`
ReadTimeout time.Duration `default:"10s" usage:"the read timeout for the HTTP request body"`
WriteTimeout time.Duration `default:"60s" usage:"the write timeout for the HTTP response body"`
MaxBodyLength string `default:"2M" usage:"the maximum number of characters that the body of an API call may contain"`
MaxTopicSubscriptionsPerClient int `default:"0" usage:"defines the max amount of subscriptions per client. 0 = deactivated (default)"`
ConfirmedStateLagThreshold uint32 `default:"2" usage:"the threshold that define a chain is unsynchronized"`
}

Enabled bool `default:"true" usage:"whether the web api plugin is enabled"`
BindAddress string `default:"0.0.0.0:9090" usage:"the bind address for the node web api"`
Auth authentication.AuthConfiguration `usage:"configures the authentication for the API service"`
IndexDbPath string `default:"waspdb/chains/index" usage:"directory for storing indexes of historical data (only archive nodes will create/use them)"`
Limits ParametersWebAPILimits
DebugRequestLoggerEnabled bool `default:"false" usage:"whether the debug logging for requests should be enabled"`
}

type ParametersWebAPILimits struct {
Timeout time.Duration `default:"30s" usage:"the timeout after which a long running operation will be canceled"`
ReadTimeout time.Duration `default:"10s" usage:"the read timeout for the HTTP request body"`
WriteTimeout time.Duration `default:"60s" usage:"the write timeout for the HTTP response body"`
MaxBodyLength string `default:"2M" usage:"the maximum number of characters that the body of an API call may contain"`
MaxTopicSubscriptionsPerClient int `default:"0" usage:"defines the max amount of subscriptions per client. 0 = deactivated (default)"`
ConfirmedStateLagThreshold uint32 `default:"2" usage:"the threshold that define a chain is unsynchronized"`
Jsonrpc ParametersJSONRPC
}

type ParametersJSONRPC struct {
MaxBlocksInLogsFilterRange int `default:"1000" usage:"maximum amount of blocks in eth_getLogs filter range"`
MaxLogsInResult int `default:"10000" usage:"maximum amount of logs in eth_getLogs result"`
}

var ParamsWebAPI = &ParametersWebAPI{
Auth: authentication.AuthConfiguration{
Scheme: "jwt",
Expand Down
10 changes: 2 additions & 8 deletions components/webapi/webapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,14 @@ func TestInternalServerErrors(t *testing.T) {
Enabled: true,
BindAddress: ":9999",
Auth: authentication.AuthConfiguration{},
Limits: struct {
Timeout time.Duration "default:\"30s\" usage:\"the timeout after which a long running operation will be canceled\""
ReadTimeout time.Duration "default:\"10s\" usage:\"the read timeout for the HTTP request body\""
WriteTimeout time.Duration "default:\"60s\" usage:\"the write timeout for the HTTP response body\""
MaxBodyLength string "default:\"2M\" usage:\"the maximum number of characters that the body of an API call may contain\""
MaxTopicSubscriptionsPerClient int "default:\"0\" usage:\"defines the max amount of subscriptions per client. 0 = deactivated (default)\""
ConfirmedStateLagThreshold uint32 "default:\"2\" usage:\"the threshold that define a chain is unsynchronized\""
}{
Limits: webapi.ParametersWebAPILimits{
Timeout: time.Minute,
ReadTimeout: time.Minute,
WriteTimeout: time.Minute,
MaxBodyLength: "1M",
MaxTopicSubscriptionsPerClient: 0,
ConfirmedStateLagThreshold: 2,
Jsonrpc: webapi.ParametersJSONRPC{},
},
DebugRequestLoggerEnabled: true,
},
Expand Down
19 changes: 10 additions & 9 deletions packages/evm/jsonrpc/evmchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ type NewBlockEvent struct {
logs []*types.Log
}

type LogsLimits struct {
MaxBlocksInLogsFilterRange int
MaxLogsInResult int
}

func NewEVMChain(
backend ChainBackend,
pub *publisher.Publisher,
Expand Down Expand Up @@ -476,16 +481,11 @@ func (e *EVMChain) BlockTransactionCountByNumber(blockNumber *big.Int) (uint64,
return uint64(len(block.Transactions())), nil
}

const (
maxBlocksInFilterRange = 1_000
maxLogsInResult = 10_000
)

// Logs executes a log filter operation, blocking during execution and
// returning all the results in one batch.
//
//nolint:gocyclo
func (e *EVMChain) Logs(query *ethereum.FilterQuery) ([]*types.Log, error) {
func (e *EVMChain) Logs(query *ethereum.FilterQuery, params *LogsLimits) ([]*types.Log, error) {
e.log.Debugf("Logs(q=%v)", query)
logs := make([]*types.Log, 0)

Expand All @@ -499,7 +499,7 @@ func (e *EVMChain) Logs(query *ethereum.FilterQuery) ([]*types.Log, error) {
}
db := blockchainDB(state)
receipts := db.GetReceiptsByBlockNumber(uint64(state.BlockIndex()))
err = filterAndAppendToLogs(query, receipts, &logs)
err = filterAndAppendToLogs(query, receipts, &logs, params.MaxLogsInResult)
if err != nil {
return nil, err
}
Expand All @@ -526,7 +526,7 @@ func (e *EVMChain) Logs(query *ethereum.FilterQuery) ([]*types.Log, error) {
{
from := from.Uint64()
to := to.Uint64()
if to > from && to-from > maxBlocksInFilterRange {
if to > from && to-from > uint64(params.MaxBlocksInLogsFilterRange) {
return nil, errors.New("too many blocks in filter range")
}
for i := from; i <= to; i++ {
Expand All @@ -538,6 +538,7 @@ func (e *EVMChain) Logs(query *ethereum.FilterQuery) ([]*types.Log, error) {
query,
blockchainDB(state).GetReceiptsByBlockNumber(i),
&logs,
params.MaxLogsInResult,
)
if err != nil {
return nil, err
Expand All @@ -547,7 +548,7 @@ func (e *EVMChain) Logs(query *ethereum.FilterQuery) ([]*types.Log, error) {
return logs, nil
}

func filterAndAppendToLogs(query *ethereum.FilterQuery, receipts []*types.Receipt, logs *[]*types.Log) error {
func filterAndAppendToLogs(query *ethereum.FilterQuery, receipts []*types.Receipt, logs *[]*types.Log, maxLogsInResult int) error {
for _, r := range receipts {
if r.Status == types.ReceiptStatusFailed {
continue
Expand Down
7 changes: 6 additions & 1 deletion packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ func newSoloTestEnv(t testing.TB) *soloTestEnv {
chain, _ := s.NewChainExt(chainOwner, 0, "chain1")

accounts := jsonrpc.NewAccountManager(nil)
rpcsrv, err := jsonrpc.NewServer(chain.EVM(), accounts, chain.GetChainMetrics().WebAPI)
rpcsrv, err := jsonrpc.NewServer(
chain.EVM(),
accounts,
chain.GetChainMetrics().WebAPI,
jsonrpc.ParametersDefault(),
)
require.NoError(t, err)
t.Cleanup(rpcsrv.Stop)

Expand Down
28 changes: 27 additions & 1 deletion packages/evm/jsonrpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,36 @@ import (
"github.com/iotaledger/wasp/packages/metrics"
)

type Parameters struct {
Logs LogsLimits
}

func NewParameters(
maxBlocksInLogsFilterRange int,
maxLogsInResult int,
) *Parameters {
return &Parameters{
Logs: LogsLimits{
MaxBlocksInLogsFilterRange: maxBlocksInLogsFilterRange,
MaxLogsInResult: maxLogsInResult,
},
}
}

func ParametersDefault() *Parameters {
return &Parameters{
Logs: LogsLimits{
MaxBlocksInLogsFilterRange: 1000,
MaxLogsInResult: 10000,
},
}
}

func NewServer(
evmChain *EVMChain,
accountManager *AccountManager,
metrics *metrics.ChainWebAPIMetrics,
params *Parameters,
) (*rpc.Server, error) {
chainID := evmChain.ChainID()
rpcsrv := rpc.NewServer()
Expand All @@ -22,7 +48,7 @@ func NewServer(
}{
{"web3", NewWeb3Service()},
{"net", NewNetService(int(chainID))},
{"eth", NewEthService(evmChain, accountManager, metrics)},
{"eth", NewEthService(evmChain, accountManager, metrics, params)},
{"debug", NewDebugService(evmChain, metrics)},
{"txpool", NewTxPoolService()},
{"evm", NewEVMService(evmChain)},
Expand Down
14 changes: 12 additions & 2 deletions packages/evm/jsonrpc/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,20 @@ type EthService struct {
evmChain *EVMChain
accounts *AccountManager
metrics *metrics.ChainWebAPIMetrics
params *Parameters
}

func NewEthService(evmChain *EVMChain, accounts *AccountManager, metrics *metrics.ChainWebAPIMetrics) *EthService {
func NewEthService(
evmChain *EVMChain,
accounts *AccountManager,
metrics *metrics.ChainWebAPIMetrics,
params *Parameters,
) *EthService {
return &EthService{
evmChain: evmChain,
accounts: accounts,
metrics: metrics,
params: params,
}
}

Expand Down Expand Up @@ -496,7 +503,10 @@ func (e *EthService) parseTxArgs(args *SendTxArgs) (*types.Transaction, error) {
}

func (e *EthService) getLogs(q *RPCFilterQuery) ([]*types.Log, error) {
logs, err := e.evmChain.Logs((*ethereum.FilterQuery)(q))
logs, err := e.evmChain.Logs(
(*ethereum.FilterQuery)(q),
&e.params.Logs,
)
if err != nil {
return nil, e.resolveError(err)
}
Expand Down
4 changes: 3 additions & 1 deletion packages/webapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/iotaledger/wasp/packages/authentication"
"github.com/iotaledger/wasp/packages/chains"
"github.com/iotaledger/wasp/packages/dkg"
"github.com/iotaledger/wasp/packages/evm/jsonrpc"
"github.com/iotaledger/wasp/packages/metrics"
"github.com/iotaledger/wasp/packages/peering"
"github.com/iotaledger/wasp/packages/publisher"
Expand Down Expand Up @@ -93,6 +94,7 @@ func Init(
websocketService *websocket.Service,
indexDbPath string,
pub *publisher.Publisher,
jsonrpcParams *jsonrpc.Parameters,
) {
// load mock files to generate correct echo swagger documentation
mocker := NewMocker()
Expand All @@ -104,7 +106,7 @@ func Init(
offLedgerService := services.NewOffLedgerService(chainService, networkProvider, requestCacheTTL)
metricsService := services.NewMetricsService(chainsProvider, chainMetricsProvider)
peeringService := services.NewPeeringService(chainsProvider, networkProvider, trustedNetworkManager)
evmService := services.NewEVMService(chainsProvider, chainService, networkProvider, pub, indexDbPath, chainMetricsProvider, logger.Named("EVMService"))
evmService := services.NewEVMService(chainsProvider, chainService, networkProvider, pub, indexDbPath, chainMetricsProvider, jsonrpcParams, logger.Named("EVMService"))
nodeService := services.NewNodeService(chainRecordRegistryProvider, nodeIdentityProvider, chainsProvider, shutdownHandler, trustedNetworkManager)
dkgService := services.NewDKGService(dkShareRegistryProvider, dkgNodeProvider, trustedNetworkManager)
userService := services.NewUserService(userManager)
Expand Down
4 changes: 4 additions & 0 deletions packages/webapi/services/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type EVMService struct {
publisher *publisher.Publisher
indexDbPath string
metrics *metrics.ChainMetricsProvider
jsonrpcParams *jsonrpc.Parameters
log *logger.Logger
}

Expand All @@ -44,6 +45,7 @@ func NewEVMService(
pub *publisher.Publisher,
indexDbPath string,
metrics *metrics.ChainMetricsProvider,
jsonrpcParams *jsonrpc.Parameters,
log *logger.Logger,
) interfaces.EVMService {
return &EVMService{
Expand All @@ -55,6 +57,7 @@ func NewEVMService(
publisher: pub,
indexDbPath: indexDbPath,
metrics: metrics,
jsonrpcParams: jsonrpcParams,
log: log,
}
}
Expand All @@ -79,6 +82,7 @@ func (e *EVMService) getEVMBackend(chainID isc.ChainID) (*chainServer, error) {
jsonrpc.NewEVMChain(backend, e.publisher, e.chainsProvider().IsArchiveNode(), hivedb.EngineRocksDB, e.indexDbPath, e.log.Named("EVMChain")),
jsonrpc.NewAccountManager(nil),
e.metrics.GetChainMetrics(chainID).WebAPI,
e.jsonrpcParams,
)
if err != nil {
return nil, err
Expand Down
3 changes: 2 additions & 1 deletion tools/api-gen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/iotaledger/wasp/components/webapi"
"github.com/iotaledger/wasp/packages/authentication"
"github.com/iotaledger/wasp/packages/cryptolib"
"github.com/iotaledger/wasp/packages/evm/jsonrpc"
v2 "github.com/iotaledger/wasp/packages/webapi"
)

Expand All @@ -35,7 +36,7 @@ func main() {
}

swagger := webapi.CreateEchoSwagger(e, app.Version)
v2.Init(mockLog, swagger, app.Version, nil, nil, nil, nil, nil, nil, &NodeIdentityProviderMock{}, nil, nil, nil, nil, authentication.AuthConfiguration{Scheme: authentication.AuthJWT}, time.Second, nil, "", nil)
v2.Init(mockLog, swagger, app.Version, nil, nil, nil, nil, nil, nil, &NodeIdentityProviderMock{}, nil, nil, nil, nil, authentication.AuthConfiguration{Scheme: authentication.AuthJWT}, time.Second, nil, "", nil, jsonrpc.ParametersDefault())

root, ok := swagger.(*echoswagger.Root)
if !ok {
Expand Down
Loading