From a16c5e20572173c6b79807afb4a7d58aea885aee Mon Sep 17 00:00:00 2001 From: romever <360876221@qq.com> Date: Mon, 30 Sep 2024 20:35:09 +0800 Subject: [PATCH] add /network/status api --- api/api.json | 242 +++++++++++++++++- .../handler/common/networkstatushandler.go | 25 ++ api/internal/handler/routes.go | 12 +- .../logic/common/networkstatuslogic.go | 78 ++++++ .../logic/validator/validatorlistlogic.go | 2 +- .../validator/validatorsignstatslogic.go | 2 +- api/internal/svc/cache.go | 6 + api/internal/types/types.go | 12 + api/oasisscan.api | 33 ++- job/model/transactionmodel.go | 15 ++ job/model/validatormodel.go | 13 + 11 files changed, 416 insertions(+), 24 deletions(-) create mode 100644 api/internal/handler/common/networkstatushandler.go create mode 100644 api/internal/logic/common/networkstatuslogic.go diff --git a/api/api.json b/api/api.json index 135eaf2..84217cc 100644 --- a/api/api.json +++ b/api/api.json @@ -556,6 +556,56 @@ ] } }, + "/v2/governance/votes": { + "get": { + "operationId": "GovernanceVotesHandler", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/GovernanceVotesResponse" + } + } + }, + "parameters": [ + { + "name": "proposalId", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "validator", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "page", + "in": "query", + "required": true, + "type": "integer", + "format": "int64", + "default": "1" + }, + { + "name": "size", + "in": "query", + "required": true, + "type": "integer", + "format": "int64", + "default": "5" + } + ], + "tags": [ + "governance" + ], + "consumes": [ + "multipart/form-data" + ] + } + }, "/v2/health": { "get": { "operationId": "HealthHandler", @@ -988,6 +1038,33 @@ "multipart/form-data" ] } + }, + "/v2/validator/signstats": { + "get": { + "operationId": "ValidatorSignStatsHandler", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/ValidatorSignStatsResponse" + } + } + }, + "parameters": [ + { + "name": "address", + "in": "query", + "required": true, + "type": "string" + } + ], + "tags": [ + "validator" + ], + "consumes": [ + "multipart/form-data" + ] + } } }, "definitions": { @@ -1463,7 +1540,7 @@ "burn": { "type": "object" }, - "Escrow": { + "escrow": { "type": "object" }, "allowanceChange": { @@ -2186,6 +2263,64 @@ "votes" ] }, + "GovernanceVotesRequest": { + "type": "object", + "properties": { + "proposalId": { + "type": "integer", + "format": "int64" + }, + "validator": { + "type": "string" + }, + "page": { + "type": "integer", + "format": "int64", + "default": "1" + }, + "size": { + "type": "integer", + "format": "int64", + "default": "5" + } + }, + "title": "GovernanceVotesRequest", + "required": [ + "page", + "size" + ] + }, + "GovernanceVotesResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/ProposalVote" + } + }, + "page": { + "type": "integer", + "format": "int64" + }, + "size": { + "type": "integer", + "format": "int64" + }, + "maxPage": { + "type": "integer", + "format": "int64" + }, + "totalSize": { + "type": "integer", + "format": "int64" + } + }, + "title": "GovernanceVotesResponse", + "required": [ + "list" + ] + }, "HealthRequest": { "type": "object", "title": "HealthRequest" @@ -2352,6 +2487,13 @@ "ProposalVote": { "type": "object", "properties": { + "proposalId": { + "type": "integer", + "format": "int64" + }, + "title": { + "type": "string" + }, "name": { "type": "string" }, @@ -2374,6 +2516,8 @@ }, "title": "ProposalVote", "required": [ + "proposalId", + "title", "name", "icon", "address", @@ -2760,6 +2904,9 @@ "RuntimeTransactionEvmTx": { "type": "object", "properties": { + "hash": { + "type": "string" + }, "from": { "type": "string" }, @@ -2787,6 +2934,7 @@ }, "title": "RuntimeTransactionEvmTx", "required": [ + "hash", "from", "to", "nonce", @@ -3176,6 +3324,12 @@ "escrowAmountStatus": { "$ref": "#/definitions/EscrowStatus" }, + "runtimes": { + "type": "array", + "items": { + "$ref": "#/definitions/ValidatorRuntime" + } + }, "status": { "type": "boolean", "format": "boolean" @@ -3212,6 +3366,7 @@ "bound", "rates", "bounds", + "runtimes", "status" ] }, @@ -3341,6 +3496,12 @@ "escrowAmountStatus": { "$ref": "#/definitions/EscrowStatus" }, + "runtimes": { + "type": "array", + "items": { + "$ref": "#/definitions/ValidatorRuntime" + } + }, "status": { "type": "boolean", "format": "boolean" @@ -3395,6 +3556,85 @@ "inactive", "delegators" ] + }, + "ValidatorRuntime": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "online": { + "type": "boolean", + "format": "boolean" + } + }, + "title": "ValidatorRuntime", + "required": [ + "name", + "id", + "online" + ] + }, + "ValidatorSignStatsInfo": { + "type": "object", + "properties": { + "dateTime": { + "type": "integer", + "format": "int64" + }, + "expected": { + "type": "integer", + "format": "int64" + }, + "actual": { + "type": "integer", + "format": "int64" + } + }, + "title": "ValidatorSignStatsInfo", + "required": [ + "dateTime", + "expected", + "actual" + ] + }, + "ValidatorSignStatsRequest": { + "type": "object", + "properties": { + "address": { + "type": "string" + } + }, + "title": "ValidatorSignStatsRequest", + "required": [ + "address" + ] + }, + "ValidatorSignStatsResponse": { + "type": "object", + "properties": { + "stats": { + "type": "array", + "items": { + "$ref": "#/definitions/ValidatorSignStatsInfo" + } + }, + "time": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + } + }, + "title": "ValidatorSignStatsResponse", + "required": [ + "stats", + "time" + ] } }, "securityDefinitions": { diff --git a/api/internal/handler/common/networkstatushandler.go b/api/internal/handler/common/networkstatushandler.go new file mode 100644 index 0000000..e4d2a16 --- /dev/null +++ b/api/internal/handler/common/networkstatushandler.go @@ -0,0 +1,25 @@ +package common + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "oasisscan-backend/api/internal/logic/common" + "oasisscan-backend/api/internal/response" + "oasisscan-backend/api/internal/svc" + "oasisscan-backend/api/internal/types" +) + +func NetworkStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.NetworkStatusRequest + if err := httpx.Parse(r, &req); err != nil { + httpx.Error(w, err) + return + } + + l := common.NewNetworkStatusLogic(r.Context(), svcCtx) + resp, err := l.NetworkStatus(&req) + response.Response(w, resp, err) + } +} diff --git a/api/internal/handler/routes.go b/api/internal/handler/routes.go index 672fd9b..afeee0e 100644 --- a/api/internal/handler/routes.go +++ b/api/internal/handler/routes.go @@ -60,7 +60,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Handler: account.AccountStakingEventsInfoHandler(serverCtx), }, }, - rest.WithPrefix("/v2"), ) server.AddRoutes( @@ -96,7 +95,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Handler: chain.ChainTransactionsHandler(serverCtx), }, }, - rest.WithPrefix("/v2"), ) server.AddRoutes( @@ -106,13 +104,17 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/health", Handler: common.HealthHandler(serverCtx), }, + { + Method: http.MethodGet, + Path: "/network/status", + Handler: common.NetworkStatusHandler(serverCtx), + }, { Method: http.MethodGet, Path: "/trend", Handler: common.NetworkTrendHandler(serverCtx), }, }, - rest.WithPrefix("/v2"), ) server.AddRoutes( @@ -133,7 +135,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Handler: governance.GovernanceVotesHandler(serverCtx), }, }, - rest.WithPrefix("/v2"), ) server.AddRoutes( @@ -149,7 +150,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Handler: market.MarketInfoHandler(serverCtx), }, }, - rest.WithPrefix("/v2"), ) server.AddRoutes( @@ -185,7 +185,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Handler: runtime.RuntimeTransactionListHandler(serverCtx), }, }, - rest.WithPrefix("/v2"), ) server.AddRoutes( @@ -221,6 +220,5 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Handler: validator.ValidatorSignStatsHandler(serverCtx), }, }, - rest.WithPrefix("/v2"), ) } diff --git a/api/internal/logic/common/networkstatuslogic.go b/api/internal/logic/common/networkstatuslogic.go new file mode 100644 index 0000000..ebbbb81 --- /dev/null +++ b/api/internal/logic/common/networkstatuslogic.go @@ -0,0 +1,78 @@ +package common + +import ( + "context" + "fmt" + "github.com/zeromicro/go-zero/core/logc" + "math/big" + "oasisscan-backend/api/internal/errort" + "oasisscan-backend/common" + + "oasisscan-backend/api/internal/svc" + "oasisscan-backend/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type NetworkStatusLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewNetworkStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NetworkStatusLogic { + return &NetworkStatusLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *NetworkStatusLogic) NetworkStatus(req *types.NetworkStatusRequest) (resp *types.NetworkStatusResponse, err error) { + v, err := l.svcCtx.LocalCache.NetworkStatusCache.Take("status", func() (interface{}, error) { + chainStatus, err := l.svcCtx.Consensus.GetStatus(l.ctx) + if err != nil { + logc.Errorf(l.ctx, "GetStatus error, %v", err) + return nil, errort.NewDefaultError() + } + latestTx, err := l.svcCtx.TransactionModel.LatestTx(l.ctx) + if err != nil { + logc.Errorf(l.ctx, "LatestTx error, %v", err) + return nil, errort.NewDefaultError() + } + + totalEscrow, err := l.svcCtx.ValidatorModel.SumEscrow(l.ctx) + if err != nil { + logc.Errorf(l.ctx, "validator SumEscrow error, %v", err) + return nil, errort.NewDefaultError() + } + + activeValidator, err := l.svcCtx.ValidatorModel.CountActive(l.ctx) + if err != nil { + logc.Errorf(l.ctx, "validator CountActive error, %v", err) + return nil, errort.NewDefaultError() + } + + delegatorCount, err := l.svcCtx.DelegatorModel.CountDistinctDelegator(l.ctx) + if err != nil { + logc.Errorf(l.ctx, "delegator CountDistinctDelegator error, %v", err) + return nil, errort.NewDefaultError() + } + + resp = &types.NetworkStatusResponse{ + CurHeight: chainStatus.LatestHeight, + CurEpoch: int64(chainStatus.LatestEpoch), + TotalTxs: latestTx.Id, + TotalEscrow: fmt.Sprintf("%.9f", common.ValueToFloatByDecimals(big.NewInt(totalEscrow), common.Decimals)), + ActiveValidator: activeValidator, + TotalDelegator: delegatorCount, + } + return resp, nil + }) + if err != nil { + logc.Errorf(l.ctx, "cache error, %v", err) + return nil, errort.NewDefaultError() + } + resp = v.(*types.NetworkStatusResponse) + return +} diff --git a/api/internal/logic/validator/validatorlistlogic.go b/api/internal/logic/validator/validatorlistlogic.go index 7e35f50..a1e5a53 100644 --- a/api/internal/logic/validator/validatorlistlogic.go +++ b/api/internal/logic/validator/validatorlistlogic.go @@ -31,7 +31,7 @@ func NewValidatorListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Val func (l *ValidatorListLogic) ValidatorList(req *types.ValidatorListRequest) (resp *types.ValidatorListResponse, err error) { cacheKey := fmt.Sprintf("%s_%s", req.OrderBy, req.Sort) - v, err := l.svcCtx.LocalCache.MarketCache.Take(cacheKey, func() (interface{}, error) { + v, err := l.svcCtx.LocalCache.ValidatorListCache.Take(cacheKey, func() (interface{}, error) { orderBy, sortType := "escrow", "desc" switch req.OrderBy { case "escrowChange24": diff --git a/api/internal/logic/validator/validatorsignstatslogic.go b/api/internal/logic/validator/validatorsignstatslogic.go index 1f50a1e..f13c49e 100644 --- a/api/internal/logic/validator/validatorsignstatslogic.go +++ b/api/internal/logic/validator/validatorsignstatslogic.go @@ -27,7 +27,7 @@ func NewValidatorSignStatsLogic(ctx context.Context, svcCtx *svc.ServiceContext) } func (l *ValidatorSignStatsLogic) ValidatorSignStats(req *types.ValidatorSignStatsRequest) (resp *types.ValidatorSignStatsResponse, err error) { - v, err := l.svcCtx.LocalCache.MarketCache.Take(req.Address, func() (interface{}, error) { + v, err := l.svcCtx.LocalCache.ValidatorSignsStatsCache.Take(req.Address, func() (interface{}, error) { stats := make([]*types.ValidatorSignStatsInfo, 0) resp = &types.ValidatorSignStatsResponse{ Stats: stats, diff --git a/api/internal/svc/cache.go b/api/internal/svc/cache.go index d918f8f..1eaddc8 100644 --- a/api/internal/svc/cache.go +++ b/api/internal/svc/cache.go @@ -13,6 +13,7 @@ type LocalCache struct { AccountRewardFindDaysCache *collection.Cache ValidatorListCache *collection.Cache ValidatorSignsStatsCache *collection.Cache + NetworkStatusCache *collection.Cache } func NewLocalCache() *LocalCache { @@ -40,6 +41,10 @@ func NewLocalCache() *LocalCache { if err != nil { logx.Errorf("localCache error: %v\n", err) } + networkStatsCache, err := collection.NewCache(time.Minute*1, collection.WithName("network-status")) + if err != nil { + logx.Errorf("localCache error: %v\n", err) + } return &LocalCache{ MarketCache: marketCache, RuntimeStatsCache: runtimeStatsCache, @@ -47,5 +52,6 @@ func NewLocalCache() *LocalCache { AccountRewardFindDaysCache: accountRewardFindDaysCache, ValidatorListCache: validatorListCache, ValidatorSignsStatsCache: validatorSignsStatsCache, + NetworkStatusCache: networkStatsCache, } } diff --git a/api/internal/types/types.go b/api/internal/types/types.go index 6c991d5..cf6b165 100644 --- a/api/internal/types/types.go +++ b/api/internal/types/types.go @@ -344,6 +344,18 @@ type MarketInfoResponse struct { VolumeChangePct24h float64 `json:"volumeChangePct24h"` } +type NetworkStatusRequest struct { +} + +type NetworkStatusResponse struct { + CurHeight int64 `json:"curHeight"` + CurEpoch int64 `json:"curEpoch"` + TotalTxs int64 `json:"totalTxs"` + TotalEscrow string `json:"totalEscrow"` + ActiveValidator int64 `json:"activeValidator"` + TotalDelegator int64 `json:"totalDelegator"` +} + type NetworkTrendRequest struct { } diff --git a/api/oasisscan.api b/api/oasisscan.api index c37a95b..7418ce2 100644 --- a/api/oasisscan.api +++ b/api/oasisscan.api @@ -17,6 +17,15 @@ type ( Tx []*Chart `json:"tx"` Escrow []*Chart `json:"escrow"` } + NetworkStatusRequest {} + NetworkStatusResponse { + CurHeight int64 `json:"curHeight"` + CurEpoch int64 `json:"curEpoch"` + TotalTxs int64 `json:"totalTxs"` + TotalEscrow string `json:"totalEscrow"` + ActiveValidator int64 `json:"activeValidator"` + TotalDelegator int64 `json:"totalDelegator"` + } ) /** validator **/ @@ -544,8 +553,7 @@ type ( ) @server ( - prefix: /v2 - group: common + group: common ) service oasisscan-api { @handler HealthHandler @@ -553,11 +561,13 @@ service oasisscan-api { @handler NetworkTrendHandler get /trend (NetworkTrendRequest) returns (NetworkTrendResponse) + + @handler NetworkStatusHandler + get /network/status (NetworkStatusRequest) returns (NetworkStatusResponse) } @server ( - prefix: /v2 - group: validator + group: validator ) service oasisscan-api { @handler ValidatorListHandler @@ -580,8 +590,7 @@ service oasisscan-api { } @server ( - prefix: /v2 - group: account + group: account ) service oasisscan-api { @handler AccountRewardHandler @@ -610,8 +619,7 @@ service oasisscan-api { } @server ( - prefix: /v2 - group: chain + group: chain ) service oasisscan-api { @handler ChainTransactionInfoHandler @@ -634,8 +642,7 @@ service oasisscan-api { } @server ( - prefix: /v2 - group: runtime + group: runtime ) service oasisscan-api { @handler RuntimeListHandler @@ -658,8 +665,7 @@ service oasisscan-api { } @server ( - prefix: /v2 - group: market + group: market ) service oasisscan-api { @handler MarketChartHandler @@ -670,8 +676,7 @@ service oasisscan-api { } @server ( - prefix: /v2 - group: governance + group: governance ) service oasisscan-api { @handler GovernanceProposalListHandler diff --git a/job/model/transactionmodel.go b/job/model/transactionmodel.go index 79542fa..fbfec06 100644 --- a/job/model/transactionmodel.go +++ b/job/model/transactionmodel.go @@ -24,6 +24,7 @@ type ( CountTxs(ctx context.Context, height int64, address string, method string) (int64, error) TransactionCountStats(ctx context.Context, day time.Time, limit int64) ([]*TransactionCountStats, error) RefreshDailyCountsStatsView(ctx context.Context) error + LatestTx(ctx context.Context) (*Transaction, error) } customTransactionModel struct { @@ -159,3 +160,17 @@ func (m *customTransactionModel) RefreshDailyCountsStatsView(ctx context.Context _, err := m.conn.ExecCtx(ctx, query) return err } + +func (m *customTransactionModel) LatestTx(ctx context.Context) (*Transaction, error) { + var resp Transaction + query := fmt.Sprintf("select %s from %s order by id desc limit 1", transactionRows, m.table) + err := m.conn.QueryRowCtx(ctx, &resp, query) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} diff --git a/job/model/validatormodel.go b/job/model/validatormodel.go index abefe5f..79b72f7 100644 --- a/job/model/validatormodel.go +++ b/job/model/validatormodel.go @@ -19,6 +19,7 @@ type ( FindOneByEntityAddress(ctx context.Context, address string) (*Validator, error) FindOneByNodeAddress(ctx context.Context, nodeAddress string) (*Validator, error) SumEscrow(ctx context.Context) (int64, error) + CountActive(ctx context.Context) (int64, error) } customValidatorModel struct { @@ -100,3 +101,15 @@ func (m *customValidatorModel) SumEscrow(ctx context.Context) (int64, error) { return 0, err } } + +func (m *customValidatorModel) CountActive(ctx context.Context) (int64, error) { + var resp int64 + query := fmt.Sprintf("select count(*) from %s where nodes=1", m.table) + err := m.conn.QueryRowCtx(ctx, &resp, query) + switch err { + case nil: + return resp, nil + default: + return 0, err + } +}