diff --git a/disperser/dataapi/docs/docs.go b/disperser/dataapi/docs/docs.go index c43e4b8876..91928052d1 100644 --- a/disperser/dataapi/docs/docs.go +++ b/disperser/dataapi/docs/docs.go @@ -374,6 +374,12 @@ const docTemplate = `{ "description": "Interval to query for operators nonsigning percentage [default: 3600]", "name": "interval", "in": "query" + }, + { + "type": "string", + "description": "End time (UTC) to query for operators nonsigning percentage", + "name": "end", + "in": "query" } ], "responses": { @@ -497,6 +503,9 @@ const docTemplate = `{ } }, "definitions": { + "big.Int": { + "type": "object" + }, "core.SecurityParam": { "type": "object", "properties": { @@ -645,12 +654,16 @@ const docTemplate = `{ }, "total_stake": { "description": "deprecated: use TotalStakePerQuorum instead. Remove when the frontend is updated.", - "type": "integer" + "allOf": [ + { + "$ref": "#/definitions/big.Int" + } + ] }, "total_stake_per_quorum": { "type": "object", "additionalProperties": { - "type": "integer" + "$ref": "#/definitions/big.Int" } } } diff --git a/disperser/dataapi/docs/swagger.json b/disperser/dataapi/docs/swagger.json index d86dd6a816..0201fdfcd7 100644 --- a/disperser/dataapi/docs/swagger.json +++ b/disperser/dataapi/docs/swagger.json @@ -370,6 +370,12 @@ "description": "Interval to query for operators nonsigning percentage [default: 3600]", "name": "interval", "in": "query" + }, + { + "type": "string", + "description": "End time (UTC) to query for operators nonsigning percentage", + "name": "end", + "in": "query" } ], "responses": { @@ -493,6 +499,9 @@ } }, "definitions": { + "big.Int": { + "type": "object" + }, "core.SecurityParam": { "type": "object", "properties": { @@ -641,12 +650,16 @@ }, "total_stake": { "description": "deprecated: use TotalStakePerQuorum instead. Remove when the frontend is updated.", - "type": "integer" + "allOf": [ + { + "$ref": "#/definitions/big.Int" + } + ] }, "total_stake_per_quorum": { "type": "object", "additionalProperties": { - "type": "integer" + "$ref": "#/definitions/big.Int" } } } diff --git a/disperser/dataapi/docs/swagger.yaml b/disperser/dataapi/docs/swagger.yaml index 0c7fb1dd04..db8aff5f51 100644 --- a/disperser/dataapi/docs/swagger.yaml +++ b/disperser/dataapi/docs/swagger.yaml @@ -1,4 +1,6 @@ definitions: + big.Int: + type: object core.SecurityParam: properties: adversaryThreshold: @@ -103,12 +105,13 @@ definitions: throughput: type: number total_stake: + allOf: + - $ref: '#/definitions/big.Int' description: 'deprecated: use TotalStakePerQuorum instead. Remove when the frontend is updated.' - type: integer total_stake_per_quorum: additionalProperties: - type: integer + $ref: '#/definitions/big.Int' type: object type: object dataapi.NonSigner: @@ -451,6 +454,10 @@ paths: in: query name: interval type: integer + - description: End time (UTC) to query for operators nonsigning percentage + in: query + name: end + type: string produces: - application/json responses: diff --git a/disperser/dataapi/nonsigner_handler.go b/disperser/dataapi/nonsigner_handler.go index da9eee24e4..8348f34a4a 100644 --- a/disperser/dataapi/nonsigner_handler.go +++ b/disperser/dataapi/nonsigner_handler.go @@ -12,8 +12,8 @@ import ( "github.com/Layr-Labs/eigenda/core/eth" ) -func (s *server) getOperatorNonsigningRate(ctx context.Context, intervalSeconds int64) (*OperatorsNonsigningPercentage, error) { - batches, err := s.subgraphClient.QueryBatchNonSigningInfoInInterval(ctx, intervalSeconds) +func (s *server) getOperatorNonsigningRate(ctx context.Context, startTime, endTime int64) (*OperatorsNonsigningPercentage, error) { + batches, err := s.subgraphClient.QueryBatchNonSigningInfoInInterval(ctx, startTime, endTime) if err != nil { return nil, err } diff --git a/disperser/dataapi/server.go b/disperser/dataapi/server.go index 79f0e389f2..754eb6204b 100644 --- a/disperser/dataapi/server.go +++ b/disperser/dataapi/server.go @@ -465,7 +465,8 @@ func (s *server) FetchNonSigners(c *gin.Context) { // @Summary Fetch operators non signing percentage // @Tags Metrics // @Produce json -// @Param interval query int false "Interval to query for operators nonsigning percentage [default: 3600]" +// @Param interval query int false "Interval to query for operators nonsigning percentage [default: 3600]" +// @Param end query string false "End time (2006-01-02T15:04:05Z) to query for operators nonsigning percentage [default: now]" // @Success 200 {object} OperatorsNonsigningPercentage // @Failure 400 {object} ErrorResponse "error: Bad request" // @Failure 404 {object} ErrorResponse "error: Not found" @@ -477,11 +478,25 @@ func (s *server) FetchOperatorsNonsigningPercentageHandler(c *gin.Context) { })) defer timer.ObserveDuration() + endTime := time.Now() + if c.Query("end") != "" { + + var err error + endTime, err = time.Parse("2006-01-02T15:04:05Z", c.Query("end")) + if err != nil { + errorResponse(c, err) + return + } + } + interval, err := strconv.ParseInt(c.DefaultQuery("interval", "3600"), 10, 64) if err != nil || interval == 0 { interval = 3600 } - metric, err := s.getOperatorNonsigningRate(c.Request.Context(), interval) + + startTime := endTime.Add(-time.Duration(interval) * time.Second) + + metric, err := s.getOperatorNonsigningRate(c.Request.Context(), startTime.Unix(), endTime.Unix()) if err != nil { s.metrics.IncrementFailedRequestNum("FetchOperatorsNonsigningPercentageHandler") errorResponse(c, err) diff --git a/disperser/dataapi/server_test.go b/disperser/dataapi/server_test.go index 6bf3ed15a1..4c295e11dd 100644 --- a/disperser/dataapi/server_test.go +++ b/disperser/dataapi/server_test.go @@ -316,7 +316,11 @@ func TestFetchMetricsThroughputHandler(t *testing.T) { func TestFetchUnsignedBatchesHandler(t *testing.T) { r := setUpRouter() - mockSubgraphApi.On("QueryBatchNonSigningInfo").Return(batchNonSigningInfo, nil) + stopTime := time.Now() + interval := 3600 + startTime := stopTime.Add(-time.Duration(interval) * time.Second) + + mockSubgraphApi.On("QueryBatchNonSigningInfo", startTime.Unix(), stopTime.Unix()).Return(batchNonSigningInfo, nil) addr1 := gethcommon.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa") addr2 := gethcommon.HexToAddress("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2") mockTx.On("BatchOperatorIDToAddress").Return([]gethcommon.Address{addr1, addr2}, nil) @@ -327,7 +331,8 @@ func TestFetchUnsignedBatchesHandler(t *testing.T) { r.GET("/v1/metrics/operator-nonsigning-percentage", testDataApiServer.FetchOperatorsNonsigningPercentageHandler) w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, "/v1/metrics/operator-nonsigning-percentage", nil) + reqStr := fmt.Sprintf("/v1/metrics/operator-nonsigning-percentage?interval=%v&end=%s", interval, stopTime.Format("2006-01-02T15:04:05Z")) + req := httptest.NewRequest(http.MethodGet, reqStr, nil) ctxWithDeadline, cancel := context.WithTimeout(req.Context(), 500*time.Microsecond) defer cancel() diff --git a/disperser/dataapi/subgraph/api.go b/disperser/dataapi/subgraph/api.go index e838ea87be..900a9f1e9c 100644 --- a/disperser/dataapi/subgraph/api.go +++ b/disperser/dataapi/subgraph/api.go @@ -23,7 +23,7 @@ type ( QueryBatchesByBlockTimestampRange(ctx context.Context, start, end uint64) ([]*Batches, error) QueryOperators(ctx context.Context, first int) ([]*Operator, error) QueryBatchNonSigningOperatorIdsInInterval(ctx context.Context, intervalSeconds int64) ([]*BatchNonSigningOperatorIds, error) - QueryBatchNonSigningInfo(ctx context.Context, intervalSeconds int64) ([]*BatchNonSigningInfo, error) + QueryBatchNonSigningInfo(ctx context.Context, startTime, endTime int64) ([]*BatchNonSigningInfo, error) QueryDeregisteredOperatorsGreaterThanBlockTimestamp(ctx context.Context, blockTimestamp uint64) ([]*Operator, error) QueryRegisteredOperatorsGreaterThanBlockTimestamp(ctx context.Context, blockTimestamp uint64) ([]*Operator, error) QueryOperatorInfoByOperatorIdAtBlockNumber(ctx context.Context, operatorId core.OperatorID, blockNumber uint32) (*IndexedOperatorInfo, error) @@ -112,10 +112,11 @@ func (a *api) QueryOperators(ctx context.Context, first int) ([]*Operator, error return result.OperatorRegistereds, nil } -func (a *api) QueryBatchNonSigningInfo(ctx context.Context, intervalSeconds int64) ([]*BatchNonSigningInfo, error) { - nonSigningAfter := time.Now().Add(-time.Duration(intervalSeconds) * time.Second).Unix() +func (a *api) QueryBatchNonSigningInfo(ctx context.Context, startTime, endTime int64) ([]*BatchNonSigningInfo, error) { + variables := map[string]any{ - "blockTimestamp_gt": graphql.Int(nonSigningAfter), + "blockTimestamp_gt": graphql.Int(startTime), + "blockTimestamp_lt": graphql.Int(endTime), } skip := 0 diff --git a/disperser/dataapi/subgraph/mock/api.go b/disperser/dataapi/subgraph/mock/api.go index a6c80b31b8..7bb152335d 100644 --- a/disperser/dataapi/subgraph/mock/api.go +++ b/disperser/dataapi/subgraph/mock/api.go @@ -67,8 +67,8 @@ func (m *MockSubgraphApi) QueryOperators(ctx context.Context, first int) ([]*sub return value, args.Error(1) } -func (m *MockSubgraphApi) QueryBatchNonSigningInfo(ctx context.Context, first int64) ([]*subgraph.BatchNonSigningInfo, error) { - args := m.Called() +func (m *MockSubgraphApi) QueryBatchNonSigningInfo(ctx context.Context, startTime, endTime int64) ([]*subgraph.BatchNonSigningInfo, error) { + args := m.Called(startTime, endTime) var value []*subgraph.BatchNonSigningInfo if args.Get(0) != nil { diff --git a/disperser/dataapi/subgraph/queries.go b/disperser/dataapi/subgraph/queries.go index 2a04527c5d..0fd0fff1fc 100644 --- a/disperser/dataapi/subgraph/queries.go +++ b/disperser/dataapi/subgraph/queries.go @@ -92,7 +92,7 @@ type ( BatchNonSigningOperatorIds []*BatchNonSigningOperatorIds `graphql:"batches(first: $first, skip: $skip, where: {blockTimestamp_gt: $blockTimestamp_gt})"` } queryBatchNonSigningInfo struct { - BatchNonSigningInfo []*BatchNonSigningInfo `graphql:"batches(first: $first, skip: $skip, where: {blockTimestamp_gt: $blockTimestamp_gt})"` + BatchNonSigningInfo []*BatchNonSigningInfo `graphql:"batches(first: $first, skip: $skip, where: {blockTimestamp_gt: $blockTimestamp_gt, blockTimestamp_lt: $blockTimestamp_lt})"` } queryOperatorRegisteredsGTBlockTimestamp struct { OperatorRegistereds []*Operator `graphql:"operatorRegistereds(orderBy: blockTimestamp, where: {blockTimestamp_gt: $blockTimestamp_gt})"` diff --git a/disperser/dataapi/subgraph_client.go b/disperser/dataapi/subgraph_client.go index 71f70150fb..749dc44b37 100644 --- a/disperser/dataapi/subgraph_client.go +++ b/disperser/dataapi/subgraph_client.go @@ -24,7 +24,7 @@ type ( QueryBatchesWithLimit(ctx context.Context, limit, skip int) ([]*Batch, error) QueryOperatorsWithLimit(ctx context.Context, limit int) ([]*Operator, error) QueryBatchNonSigningOperatorIdsInInterval(ctx context.Context, intervalSeconds int64) (map[string]int, error) - QueryBatchNonSigningInfoInInterval(ctx context.Context, intervalSeconds int64) ([]*BatchNonSigningInfo, error) + QueryBatchNonSigningInfoInInterval(ctx context.Context, startTime, endTime int64) ([]*BatchNonSigningInfo, error) QueryOperatorQuorumEvent(ctx context.Context, startBlock, endBlock uint32) (*OperatorQuorumEvents, error) QueryIndexedDeregisteredOperatorsForTimeWindow(ctx context.Context, days int32) (*IndexedDeregisteredOperatorState, error) } @@ -126,8 +126,8 @@ func (sc *subgraphClient) QueryOperatorsWithLimit(ctx context.Context, limit int return operators, nil } -func (sc *subgraphClient) QueryBatchNonSigningInfoInInterval(ctx context.Context, intervalSeconds int64) ([]*BatchNonSigningInfo, error) { - batchNonSigningInfoGql, err := sc.api.QueryBatchNonSigningInfo(ctx, intervalSeconds) +func (sc *subgraphClient) QueryBatchNonSigningInfoInInterval(ctx context.Context, startTime, endTime int64) ([]*BatchNonSigningInfo, error) { + batchNonSigningInfoGql, err := sc.api.QueryBatchNonSigningInfo(ctx, startTime, endTime) if err != nil { return nil, err } diff --git a/disperser/dataapi/subgraph_client_test.go b/disperser/dataapi/subgraph_client_test.go index 562880a4eb..d4b2bd5be4 100644 --- a/disperser/dataapi/subgraph_client_test.go +++ b/disperser/dataapi/subgraph_client_test.go @@ -487,9 +487,9 @@ func TestQueryIndexedDeregisteredOperatorsForTimeWindow(t *testing.T) { func TestQueryBatchNonSigningInfoInInterval(t *testing.T) { mockSubgraphApi := &subgraphmock.MockSubgraphApi{} - mockSubgraphApi.On("QueryBatchNonSigningInfo").Return(batchNonSigningInfo, nil) + mockSubgraphApi.On("QueryBatchNonSigningInfo", int64(0), int64(1)).Return(batchNonSigningInfo, nil) subgraphClient := dataapi.NewSubgraphClient(mockSubgraphApi, logging.NewNoopLogger()) - result, err := subgraphClient.QueryBatchNonSigningInfoInInterval(context.Background(), int64(1)) + result, err := subgraphClient.QueryBatchNonSigningInfoInInterval(context.Background(), 0, 1) assert.NoError(t, err) assert.Equal(t, 2, len(result))