diff --git a/api/api.go b/api/api.go index 773b0ff8b..d13826ddc 100644 --- a/api/api.go +++ b/api/api.go @@ -60,22 +60,25 @@ const ( // //nolint:revive const ( - ParamAccountId = "accountId" - ParamCensusId = "censusId" - ParamElectionId = "electionId" - ParamOrganizationId = "organizationId" - ParamVoteId = "voteId" - ParamPage = "page" - ParamLimit = "limit" - ParamStatus = "status" - ParamWithResults = "withResults" - ParamFinalResults = "finalResults" - ParamManuallyEnded = "manuallyEnded" - ParamHeight = "height" - ParamReference = "reference" - ParamType = "type" - ParamAccountIdFrom = "accountIdFrom" - ParamAccountIdTo = "accountIdTo" + ParamAccountId = "accountId" + ParamCensusId = "censusId" + ParamElectionId = "electionId" + ParamOrganizationId = "organizationId" + ParamVoteId = "voteId" + ParamPage = "page" + ParamLimit = "limit" + ParamStatus = "status" + ParamWithResults = "withResults" + ParamFinalResults = "finalResults" + ParamManuallyEnded = "manuallyEnded" + ParamHeight = "height" + ParamReference = "reference" + ParamType = "type" + ParamAccountIdFrom = "accountIdFrom" + ParamAccountIdTo = "accountIdTo" + ParamChainId = "chainId" + ParamHash = "hash" + ParamProposerAddress = "proposerAddress" ) var ( diff --git a/api/api_types.go b/api/api_types.go index 9521b9f48..41dc787e9 100644 --- a/api/api_types.go +++ b/api/api_types.go @@ -50,6 +50,14 @@ type TransactionParams struct { Type string `json:"type,omitempty"` } +// BlockParams allows the client to filter blocks +type BlockParams struct { + PaginationParams + ChainID string `json:"chainId,omitempty"` + Hash string `json:"hash,omitempty"` + ProposerAddress string `json:"proposerAddress,omitempty"` +} + // FeesParams allows the client to filter fees type FeesParams struct { PaginationParams @@ -436,3 +444,9 @@ type Block struct { Hash types.HexBytes `json:"hash" ` TxCount int64 `json:"txCount"` } + +// BlockList is used to return a paginated list to the client +type BlockList struct { + Blocks []*indexertypes.Block `json:"blocks"` + Pagination *Pagination `json:"pagination"` +} diff --git a/api/chain.go b/api/chain.go index 1f5ddc3d0..603035bc3 100644 --- a/api/chain.go +++ b/api/chain.go @@ -165,7 +165,7 @@ func (a *API) enableChainHandlers() error { "/chain/blocks/{height}", "GET", apirest.MethodAccessTypePublic, - a.chainBlockHandler, + a.chainBlockByHeightHandler, ); err != nil { return err } @@ -177,6 +177,14 @@ func (a *API) enableChainHandlers() error { ); err != nil { return err } + if err := a.Endpoint.RegisterMethod( + "/chain/blocks", + "GET", + apirest.MethodAccessTypePublic, + a.chainBlockListHandler, + ); err != nil { + return err + } if err := a.Endpoint.RegisterMethod( "/chain/organizations/filter/page/{page}", "POST", @@ -855,7 +863,7 @@ func (a *API) chainValidatorsHandler(_ *apirest.APIdata, ctx *httprouter.HTTPCon return ctx.Send(data, apirest.HTTPstatusOK) } -// chainBlockHandler +// chainBlockByHeightHandler // // @Summary Get block (by height) // @Description Returns the full block information at the given height @@ -865,7 +873,7 @@ func (a *API) chainValidatorsHandler(_ *apirest.APIdata, ctx *httprouter.HTTPCon // @Param height path int true "Block height" // @Success 200 {object} api.Block // @Router /chain/blocks/{height} [get] -func (a *API) chainBlockHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error { +func (a *API) chainBlockByHeightHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error { height, err := strconv.ParseUint(ctx.URLParam(ParamHeight), 10, 64) if err != nil { return err @@ -951,6 +959,63 @@ func (a *API) chainBlockByHashHandler(_ *apirest.APIdata, ctx *httprouter.HTTPCo return ctx.Send(convertKeysToCamel(data), apirest.HTTPstatusOK) } +// chainBlockListHandler +// +// @Summary List all blocks +// @Description Returns the list of blocks, ordered by descending height. +// @Tags Chain +// @Accept json +// @Produce json +// @Param page query number false "Page" +// @Param limit query number false "Items per page" +// @Param chainId query string false "Filter by exact chainId" +// @Param hash query string false "Filter by partial hash" +// @Param proposerAddress query string false "Filter by exact proposerAddress" +// @Success 200 {object} BlockList +// @Router /chain/blocks [get] +func (a *API) chainBlockListHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error { + params, err := parseBlockParams( + ctx.QueryParam(ParamPage), + ctx.QueryParam(ParamLimit), + ctx.QueryParam(ParamChainId), + ctx.QueryParam(ParamHash), + ctx.QueryParam(ParamProposerAddress), + ) + if err != nil { + return err + } + + return a.sendBlockList(ctx, params) +} + +// sendBlockList produces a filtered, paginated BlockList, +// and sends it marshalled over ctx.Send +// +// Errors returned are always of type APIerror. +func (a *API) sendBlockList(ctx *httprouter.HTTPContext, params *BlockParams) error { + blocks, total, err := a.indexer.SearchBlocks( + params.Limit, + params.Page*params.Limit, + params.ChainID, + params.Hash, + params.ProposerAddress, + ) + if err != nil { + return ErrIndexerQueryFailed.WithErr(err) + } + + pagination, err := calculatePagination(params.Page, params.Limit, total) + if err != nil { + return err + } + + list := &BlockList{ + Blocks: blocks, + Pagination: pagination, + } + return marshalAndSend(ctx, list) +} + // chainTransactionCountHandler // // @Summary Transactions count @@ -1264,3 +1329,18 @@ func parseTransactionParams(paramPage, paramLimit, paramHeight, paramType string Type: paramType, }, nil } + +// parseBlockParams returns an BlockParams filled with the passed params +func parseBlockParams(paramPage, paramLimit, paramChainId, paramHash, paramProposerAddress string) (*BlockParams, error) { + pagination, err := parsePaginationParams(paramPage, paramLimit) + if err != nil { + return nil, err + } + + return &BlockParams{ + PaginationParams: pagination, + ChainID: paramChainId, + Hash: util.TrimHex(paramHash), + ProposerAddress: util.TrimHex(paramProposerAddress), + }, nil +}