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/api blocklist #1369

Merged
merged 2 commits into from
Aug 23, 2024
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
3 changes: 3 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ const (
ParamWithResults = "withResults"
ParamFinalResults = "finalResults"
ParamManuallyEnded = "manuallyEnded"
ParamChainId = "chainId"
ParamHash = "hash"
ParamProposerAddress = "proposerAddress"
ParamHeight = "height"
ParamReference = "reference"
ParamType = "type"
Expand Down
14 changes: 14 additions & 0 deletions api/api_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,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
Expand Down Expand Up @@ -439,3 +447,9 @@ type Block struct {
comettypes.Block `json:",inline"`
Hash types.HexBytes `json:"hash" `
}

// BlockList is used to return a paginated list to the client
type BlockList struct {
Blocks []*indexertypes.Block `json:"blocks"`
Pagination *Pagination `json:"pagination"`
}
113 changes: 113 additions & 0 deletions api/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"go.vocdoni.io/dvote/vochain"
"go.vocdoni.io/dvote/vochain/genesis"
"go.vocdoni.io/dvote/vochain/indexer"
"go.vocdoni.io/dvote/vochain/indexer/indexertypes"
"go.vocdoni.io/dvote/vochain/state"
)

Expand Down Expand Up @@ -178,6 +179,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",
Expand Down Expand Up @@ -972,6 +981,95 @@ 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 {
// TODO: replace this by a.indexer.BlockList when it's available
blockList := func(limit, offset int, _, _, _ string) ([]*indexertypes.Block, uint64, error) {
if offset < 0 {
return nil, 0, fmt.Errorf("invalid value: offset cannot be %d", offset)
}
if limit <= 0 {
return nil, 0, fmt.Errorf("invalid value: limit cannot be %d", limit)
}
height := a.vocapp.Height()
total := uint64(height) - uint64(a.vocapp.Node.BlockStore().Base())
start := height - uint32(params.Page*params.Limit)
end := start - uint32(params.Limit)
list := []*indexertypes.Block{}
for h := start; h > end; h-- {
tmblock := a.vocapp.GetBlockByHeight(int64(h))
if tmblock == nil {
break
}
list = append(list, &indexertypes.Block{
ChainID: tmblock.ChainID,
Height: tmblock.Height,
Time: tmblock.Time,
Hash: types.HexBytes(tmblock.Hash()),
ProposerAddress: tmblock.ProposerAddress.Bytes(),
LastBlockHash: tmblock.LastBlockID.Hash.Bytes(),
TxCount: int64(len(tmblock.Txs)),
})
}

return list, uint64(total), nil
}

blocks, total, err := blockList(
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
Expand Down Expand Up @@ -1320,3 +1418,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
}
20 changes: 20 additions & 0 deletions vochain/indexer/indexertypes/block.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package indexertypes

import (
"time"

"go.vocdoni.io/dvote/types"
)

// Block represents a block handled by the Vochain.
// The indexer Block data type is different from the vochain state data type
// since it is optimized for querying purposes and not for keeping a shared consensus state.
type Block struct {
ChainID string `json:"chainId"`
Height int64 `json:"height"`
Time time.Time `json:"time"`
Hash types.HexBytes `json:"hash"`
ProposerAddress types.HexBytes `json:"proposer"`
LastBlockHash types.HexBytes `json:"lastBlockHash"`
TxCount int64 `json:"txCount"`
}
Loading