From 45c41d161d813e726a88cf5964cd2b78caa04372 Mon Sep 17 00:00:00 2001 From: Diego Essaya Date: Tue, 3 Oct 2023 17:23:05 -0300 Subject: [PATCH 1/2] feat(jsonrpc): configurable eth_getLogs limits --- components/app/app.go | 2 ++ components/jsonrpc/component.go | 15 +++++++++++++++ packages/evm/jsonrpc/evmchain.go | 14 +++++--------- packages/evm/jsonrpc/server.go | 10 ++++++++++ packages/evm/jsonrpc/service.go | 6 +++++- 5 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 components/jsonrpc/component.go diff --git a/components/app/app.go b/components/app/app.go index 2b544d41e2..cef1599f40 100644 --- a/components/app/app.go +++ b/components/app/app.go @@ -10,6 +10,7 @@ import ( "github.com/iotaledger/wasp/components/chains" "github.com/iotaledger/wasp/components/database" "github.com/iotaledger/wasp/components/dkg" + "github.com/iotaledger/wasp/components/jsonrpc" "github.com/iotaledger/wasp/components/logger" "github.com/iotaledger/wasp/components/nodeconn" "github.com/iotaledger/wasp/components/peering" @@ -51,6 +52,7 @@ func App() *app.App { wasmtimevm.Component, chains.Component, publisher.Component, + jsonrpc.Component, webapi.Component, profiling.Component, profilingrecorder.Component, diff --git a/components/jsonrpc/component.go b/components/jsonrpc/component.go new file mode 100644 index 0000000000..a581721ca5 --- /dev/null +++ b/components/jsonrpc/component.go @@ -0,0 +1,15 @@ +package jsonrpc + +import ( + "github.com/iotaledger/hive.go/app" + "github.com/iotaledger/wasp/packages/evm/jsonrpc" +) + +var Component = &app.Component{ + Name: "JSONRPC", + Params: &app.ComponentParams{ + Params: map[string]any{ + "jsonrpc": jsonrpc.Params, + }, + }, +} diff --git a/packages/evm/jsonrpc/evmchain.go b/packages/evm/jsonrpc/evmchain.go index 4759183599..7581ec7219 100644 --- a/packages/evm/jsonrpc/evmchain.go +++ b/packages/evm/jsonrpc/evmchain.go @@ -476,16 +476,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, maxBlocksInFilterRange, maxLogsInResult int) ([]*types.Log, error) { e.log.Debugf("Logs(q=%v)", query) logs := make([]*types.Log, 0) @@ -499,7 +494,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, maxLogsInResult) if err != nil { return nil, err } @@ -526,7 +521,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(maxBlocksInFilterRange) { return nil, errors.New("too many blocks in filter range") } for i := from; i <= to; i++ { @@ -538,6 +533,7 @@ func (e *EVMChain) Logs(query *ethereum.FilterQuery) ([]*types.Log, error) { query, blockchainDB(state).GetReceiptsByBlockNumber(i), &logs, + maxLogsInResult, ) if err != nil { return nil, err @@ -547,7 +543,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 diff --git a/packages/evm/jsonrpc/server.go b/packages/evm/jsonrpc/server.go index b62e012624..467342ae7a 100644 --- a/packages/evm/jsonrpc/server.go +++ b/packages/evm/jsonrpc/server.go @@ -9,6 +9,16 @@ import ( "github.com/iotaledger/wasp/packages/metrics" ) +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 Params = &ParametersJSONRPC{ + MaxBlocksInLogsFilterRange: 1000, + MaxLogsInResult: 10000, +} + func NewServer( evmChain *EVMChain, accountManager *AccountManager, diff --git a/packages/evm/jsonrpc/service.go b/packages/evm/jsonrpc/service.go index 5209dfb0ca..9dbe7b5b29 100644 --- a/packages/evm/jsonrpc/service.go +++ b/packages/evm/jsonrpc/service.go @@ -496,7 +496,11 @@ 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), + Params.MaxBlocksInLogsFilterRange, + Params.MaxLogsInResult, + ) if err != nil { return nil, e.resolveError(err) } From 30736ff6c9260854af76cf5efe45ee1d4e4a9b7e Mon Sep 17 00:00:00 2001 From: Diego Essaya Date: Fri, 6 Oct 2023 11:04:37 -0300 Subject: [PATCH 2/2] pass jsonrpc params by constructor --- components/app/app.go | 2 -- components/jsonrpc/component.go | 15 --------- components/webapi/component.go | 5 +++ components/webapi/params.go | 33 +++++++++++-------- components/webapi/webapi_test.go | 10 ++---- packages/evm/jsonrpc/evmchain.go | 13 +++++--- .../evm/jsonrpc/jsonrpctest/jsonrpc_test.go | 7 +++- packages/evm/jsonrpc/server.go | 30 +++++++++++++---- packages/evm/jsonrpc/service.go | 12 +++++-- packages/webapi/api.go | 4 ++- packages/webapi/services/evm.go | 4 +++ tools/api-gen/main.go | 3 +- 12 files changed, 83 insertions(+), 55 deletions(-) delete mode 100644 components/jsonrpc/component.go diff --git a/components/app/app.go b/components/app/app.go index cef1599f40..2b544d41e2 100644 --- a/components/app/app.go +++ b/components/app/app.go @@ -10,7 +10,6 @@ import ( "github.com/iotaledger/wasp/components/chains" "github.com/iotaledger/wasp/components/database" "github.com/iotaledger/wasp/components/dkg" - "github.com/iotaledger/wasp/components/jsonrpc" "github.com/iotaledger/wasp/components/logger" "github.com/iotaledger/wasp/components/nodeconn" "github.com/iotaledger/wasp/components/peering" @@ -52,7 +51,6 @@ func App() *app.App { wasmtimevm.Component, chains.Component, publisher.Component, - jsonrpc.Component, webapi.Component, profiling.Component, profilingrecorder.Component, diff --git a/components/jsonrpc/component.go b/components/jsonrpc/component.go deleted file mode 100644 index a581721ca5..0000000000 --- a/components/jsonrpc/component.go +++ /dev/null @@ -1,15 +0,0 @@ -package jsonrpc - -import ( - "github.com/iotaledger/hive.go/app" - "github.com/iotaledger/wasp/packages/evm/jsonrpc" -) - -var Component = &app.Component{ - Name: "JSONRPC", - Params: &app.ComponentParams{ - Params: map[string]any{ - "jsonrpc": jsonrpc.Params, - }, - }, -} diff --git a/components/webapi/component.go b/components/webapi/component.go index 25513d4371..cc11146731 100644 --- a/components/webapi/component.go +++ b/components/webapi/component.go @@ -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" @@ -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{ diff --git a/components/webapi/params.go b/components/webapi/params.go index bafe46fe3d..18a6ed6ed6 100644 --- a/components/webapi/params.go +++ b/components/webapi/params.go @@ -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", diff --git a/components/webapi/webapi_test.go b/components/webapi/webapi_test.go index 4248bc11f8..2d7d8ca1ca 100644 --- a/components/webapi/webapi_test.go +++ b/components/webapi/webapi_test.go @@ -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, }, diff --git a/packages/evm/jsonrpc/evmchain.go b/packages/evm/jsonrpc/evmchain.go index 7581ec7219..4150a28630 100644 --- a/packages/evm/jsonrpc/evmchain.go +++ b/packages/evm/jsonrpc/evmchain.go @@ -57,6 +57,11 @@ type NewBlockEvent struct { logs []*types.Log } +type LogsLimits struct { + MaxBlocksInLogsFilterRange int + MaxLogsInResult int +} + func NewEVMChain( backend ChainBackend, pub *publisher.Publisher, @@ -480,7 +485,7 @@ func (e *EVMChain) BlockTransactionCountByNumber(blockNumber *big.Int) (uint64, // returning all the results in one batch. // //nolint:gocyclo -func (e *EVMChain) Logs(query *ethereum.FilterQuery, maxBlocksInFilterRange, maxLogsInResult int) ([]*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) @@ -494,7 +499,7 @@ func (e *EVMChain) Logs(query *ethereum.FilterQuery, maxBlocksInFilterRange, max } db := blockchainDB(state) receipts := db.GetReceiptsByBlockNumber(uint64(state.BlockIndex())) - err = filterAndAppendToLogs(query, receipts, &logs, maxLogsInResult) + err = filterAndAppendToLogs(query, receipts, &logs, params.MaxLogsInResult) if err != nil { return nil, err } @@ -521,7 +526,7 @@ func (e *EVMChain) Logs(query *ethereum.FilterQuery, maxBlocksInFilterRange, max { from := from.Uint64() to := to.Uint64() - if to > from && to-from > uint64(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++ { @@ -533,7 +538,7 @@ func (e *EVMChain) Logs(query *ethereum.FilterQuery, maxBlocksInFilterRange, max query, blockchainDB(state).GetReceiptsByBlockNumber(i), &logs, - maxLogsInResult, + params.MaxLogsInResult, ) if err != nil { return nil, err diff --git a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go index 44a03c3407..0081d21926 100644 --- a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go +++ b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go @@ -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) diff --git a/packages/evm/jsonrpc/server.go b/packages/evm/jsonrpc/server.go index 467342ae7a..24930353e3 100644 --- a/packages/evm/jsonrpc/server.go +++ b/packages/evm/jsonrpc/server.go @@ -9,20 +9,36 @@ import ( "github.com/iotaledger/wasp/packages/metrics" ) -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"` +type Parameters struct { + Logs LogsLimits } -var Params = &ParametersJSONRPC{ - MaxBlocksInLogsFilterRange: 1000, - MaxLogsInResult: 10000, +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() @@ -32,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)}, diff --git a/packages/evm/jsonrpc/service.go b/packages/evm/jsonrpc/service.go index 9dbe7b5b29..ea7adbdcf4 100644 --- a/packages/evm/jsonrpc/service.go +++ b/packages/evm/jsonrpc/service.go @@ -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, } } @@ -498,8 +505,7 @@ 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), - Params.MaxBlocksInLogsFilterRange, - Params.MaxLogsInResult, + &e.params.Logs, ) if err != nil { return nil, e.resolveError(err) diff --git a/packages/webapi/api.go b/packages/webapi/api.go index b6e4b37e9b..9389fab530 100644 --- a/packages/webapi/api.go +++ b/packages/webapi/api.go @@ -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" @@ -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() @@ -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) diff --git a/packages/webapi/services/evm.go b/packages/webapi/services/evm.go index f4c1677f9b..5a12eebe6c 100644 --- a/packages/webapi/services/evm.go +++ b/packages/webapi/services/evm.go @@ -34,6 +34,7 @@ type EVMService struct { publisher *publisher.Publisher indexDbPath string metrics *metrics.ChainMetricsProvider + jsonrpcParams *jsonrpc.Parameters log *logger.Logger } @@ -44,6 +45,7 @@ func NewEVMService( pub *publisher.Publisher, indexDbPath string, metrics *metrics.ChainMetricsProvider, + jsonrpcParams *jsonrpc.Parameters, log *logger.Logger, ) interfaces.EVMService { return &EVMService{ @@ -55,6 +57,7 @@ func NewEVMService( publisher: pub, indexDbPath: indexDbPath, metrics: metrics, + jsonrpcParams: jsonrpcParams, log: log, } } @@ -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 diff --git a/tools/api-gen/main.go b/tools/api-gen/main.go index 2b8f179523..31b29a4404 100644 --- a/tools/api-gen/main.go +++ b/tools/api-gen/main.go @@ -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" ) @@ -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 {