Skip to content

Commit

Permalink
api: fetch blocks and transactions from indexer rather than app Block…
Browse files Browse the repository at this point in the history
…Store

these endpoints now fetch blocks from indexer, return just `header` and include `txCount`
 * /chain/blocks
 * /chain/blocks/{height}
 * /chain/blocks/hash/{hash}

this endpoint now fetches the full tx from indexer, and includes `subtype` and `signer` fields
 * /chain/transactions/{height}/{index}

this endpoint now accepts more filter params (subtype, signer)
 * /chain/transactions

refactor:
* api: rename chainBlockHandler -> chainBlockByHeightHandler
  • Loading branch information
altergui committed Aug 27, 2024
1 parent 1eaf21a commit bc896d2
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 76 deletions.
2 changes: 2 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ const (
ParamHeight = "height"
ParamReference = "reference"
ParamType = "type"
ParamSubtype = "subtype"
ParamSigner = "signer"
ParamAccountIdFrom = "accountIdFrom"
ParamAccountIdTo = "accountIdTo"
ParamStartDateAfter = "startDateAfter"
Expand Down
18 changes: 11 additions & 7 deletions api/api_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ type AccountParams struct {
// TransactionParams allows the client to filter transactions
type TransactionParams struct {
PaginationParams
Height uint64 `json:"height,omitempty"`
Type string `json:"type,omitempty"`
Height uint64 `json:"height,omitempty"`
Type string `json:"type,omitempty"`
Subtype string `json:"subtype,omitempty"`
Signer string `json:"signer,omitempty"`
}

// BlockParams allows the client to filter blocks
Expand Down Expand Up @@ -292,9 +294,10 @@ type TransfersList struct {
}

type GenericTransactionWithInfo struct {
TxContent json.RawMessage `json:"tx"`
TxInfo indexertypes.Transaction `json:"txInfo"`
Signature types.HexBytes `json:"signature"`
TxContent json.RawMessage `json:"tx"`
TxInfo *indexertypes.Transaction `json:"txInfo"`
Signature types.HexBytes `json:"signature"`
Signer types.HexBytes `json:"signer"`
}

type ChainInfo struct {
Expand Down Expand Up @@ -444,8 +447,9 @@ func CensusTypeToOrigin(ctype CensusTypeDescription) (models.CensusOrigin, []byt
}

type Block struct {
comettypes.Block `json:",inline"`
Hash types.HexBytes `json:"hash" `
comettypes.Header `json:"header"`
Hash types.HexBytes `json:"hash" `
TxCount int64 `json:"txCount"`
}

// BlockList is used to return a paginated list to the client
Expand Down
130 changes: 61 additions & 69 deletions api/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@ import (
"go.vocdoni.io/dvote/crypto/zk/circuit"
"go.vocdoni.io/dvote/httprouter"
"go.vocdoni.io/dvote/httprouter/apirest"
"go.vocdoni.io/dvote/types"
"go.vocdoni.io/dvote/util"
"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 @@ -167,7 +164,7 @@ func (a *API) enableChainHandlers() error {
"/chain/blocks/{height}",
"GET",
apirest.MethodAccessTypePublic,
a.chainBlockHandler,
a.chainBlockByHeightHandler,
); err != nil {
return err
}
Expand Down Expand Up @@ -690,14 +687,6 @@ func (a *API) chainTxHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) er
if err != nil {
return err
}
stx, err := a.vocapp.GetTx(uint32(height), int32(index))
if err != nil {
if errors.Is(err, vochain.ErrTransactionNotFound) {
return ErrTransactionNotFound
}
return ErrVochainGetTxFailed.WithErr(err)
}

ref, err := a.indexer.GetTransactionByHeightAndIndex(height, index)
if err != nil {
if errors.Is(err, indexer.ErrTransactionNotFound) {
Expand All @@ -706,9 +695,10 @@ func (a *API) chainTxHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) er
return ErrVochainGetTxFailed.WithErr(err)
}
tx := &GenericTransactionWithInfo{
TxContent: []byte(protoFormat(stx.Tx)),
Signature: stx.Signature,
TxInfo: *ref,
TxContent: []byte(protoFormat(ref.RawTx)),
TxInfo: ref,
Signature: ref.Signature,
Signer: ref.Signer,
}
data, err := json.Marshal(tx)
if err != nil {
Expand Down Expand Up @@ -758,6 +748,8 @@ func (a *API) chainTxRefByIndexHandler(_ *apirest.APIdata, ctx *httprouter.HTTPC
// @Param limit query number false "Items per page"
// @Param height query number false "Block height"
// @Param type query string false "Tx type"
// @Param subtype query string false "Tx subtype"
// @Param signer query string false "Tx signer"
// @Success 200 {object} TransactionsList "List of transactions references"
// @Router /chain/transactions [get]
func (a *API) chainTxListHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error {
Expand All @@ -766,6 +758,8 @@ func (a *API) chainTxListHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext
ctx.QueryParam(ParamLimit),
ctx.QueryParam(ParamHeight),
ctx.QueryParam(ParamType),
ctx.QueryParam(ParamSubtype),
ctx.QueryParam(ParamSigner),
)
if err != nil {
return err
Expand Down Expand Up @@ -797,6 +791,8 @@ func (a *API) chainTxListByPageHandler(_ *apirest.APIdata, ctx *httprouter.HTTPC
"",
"",
"",
"",
"",
)
if err != nil {
return err
Expand Down Expand Up @@ -833,6 +829,8 @@ func (a *API) chainTxListByHeightAndPageHandler(_ *apirest.APIdata, ctx *httprou
"",
ctx.URLParam(ParamHeight),
"",
"",
"",
)
if err != nil {
return err
Expand All @@ -859,6 +857,8 @@ func (a *API) transactionList(params *TransactionParams) (*TransactionsList, err
params.Page*params.Limit,
params.Height,
params.Type,
params.Subtype,
params.Signer,
)
if err != nil {
return nil, ErrIndexerQueryFailed.WithErr(err)
Expand Down Expand Up @@ -911,7 +911,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
Expand All @@ -921,23 +921,34 @@ 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
}
tmblock := a.vocapp.GetBlockByHeight(int64(height))
if tmblock == nil {
return ErrBlockNotFound
idxblock, err := a.indexer.BlockByHeight(int64(height))
if err != nil {
if errors.Is(err, indexer.ErrBlockNotFound) {
return ErrBlockNotFound
}
return ErrBlockNotFound.WithErr(err)
}
txcount, err := a.indexer.CountTransactionsByHeight(int64(height))
if err != nil {
return ErrIndexerQueryFailed.WithErr(err)
}
block := &Block{
Block: comettypes.Block{
Header: tmblock.Header,
Data: tmblock.Data,
Evidence: tmblock.Evidence,
LastCommit: tmblock.LastCommit,
Header: comettypes.Header{
ChainID: idxblock.ChainID,
Height: idxblock.Height,
Time: idxblock.Time,
ProposerAddress: []byte(idxblock.ProposerAddress),
LastBlockID: comettypes.BlockID{
Hash: []byte(idxblock.LastBlockHash),
},
},
Hash: types.HexBytes(tmblock.Hash()),
Hash: idxblock.Hash,
TxCount: txcount,
}
data, err := json.Marshal(block)
if err != nil {
Expand All @@ -961,18 +972,29 @@ func (a *API) chainBlockByHashHandler(_ *apirest.APIdata, ctx *httprouter.HTTPCo
if err != nil {
return err
}
tmblock := a.vocapp.GetBlockByHash(hash)
if tmblock == nil {
return ErrBlockNotFound
idxblock, err := a.indexer.BlockByHash(hash)
if err != nil {
if errors.Is(err, indexer.ErrBlockNotFound) {
return ErrBlockNotFound
}
return ErrBlockNotFound.WithErr(err)
}
txcount, err := a.indexer.CountTransactionsByHeight(idxblock.Height)
if err != nil {
return ErrIndexerQueryFailed.WithErr(err)
}
block := &Block{
Block: comettypes.Block{
Header: tmblock.Header,
Data: tmblock.Data,
Evidence: tmblock.Evidence,
LastCommit: tmblock.LastCommit,
Header: comettypes.Header{
ChainID: idxblock.ChainID,
Height: idxblock.Height,
Time: idxblock.Time,
ProposerAddress: []byte(idxblock.ProposerAddress),
LastBlockID: comettypes.BlockID{
Hash: []byte(idxblock.LastBlockHash),
},
},
Hash: types.HexBytes(tmblock.Hash()),
Hash: idxblock.Hash,
TxCount: txcount,
}
data, err := json.Marshal(block)
if err != nil {
Expand Down Expand Up @@ -1015,39 +1037,7 @@ func (a *API) chainBlockListHandler(_ *apirest.APIdata, ctx *httprouter.HTTPCont
//
// 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(
blocks, total, err := a.indexer.BlockList(
params.Limit,
params.Page*params.Limit,
params.ChainID,
Expand Down Expand Up @@ -1401,7 +1391,7 @@ func parseTransfersParams(paramPage, paramLimit, paramAccountId, paramAccountIdF
}

// parseTransactionParams returns an TransactionParams filled with the passed params
func parseTransactionParams(paramPage, paramLimit, paramHeight, paramType string) (*TransactionParams, error) {
func parseTransactionParams(paramPage, paramLimit, paramHeight, paramType, paramSubtype, paramSigner string) (*TransactionParams, error) {
pagination, err := parsePaginationParams(paramPage, paramLimit)
if err != nil {
return nil, err
Expand All @@ -1416,6 +1406,8 @@ func parseTransactionParams(paramPage, paramLimit, paramHeight, paramType string
PaginationParams: pagination,
Height: uint64(height),
Type: paramType,
Subtype: paramSubtype,
Signer: paramSigner,
}, nil
}

Expand Down
35 changes: 35 additions & 0 deletions test/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,41 @@ func TestAPIAccountTokentxs(t *testing.T) {
qt.Assert(t, gotAcct1.Balance, qt.Equals, initBalance+amountAcc2toAcct1-amountAcc1toAcct2-uint64(txBasePrice))
}

func TestAPIBlocks(t *testing.T) {
server := testcommon.APIserver{}
server.Start(t,
api.ChainHandler,
api.CensusHandler,
api.VoteHandler,
api.AccountHandler,
api.ElectionHandler,
api.WalletHandler,
)
token1 := uuid.New()
c := testutil.NewTestHTTPclient(t, server.ListenAddr, &token1)

// Block 1
server.VochainAPP.AdvanceTestBlock()
waitUntilHeight(t, c, 1)

// create a new account
initBalance := uint64(80)
_ = createAccount(t, c, server, initBalance)

// Block 2
server.VochainAPP.AdvanceTestBlock()
waitUntilHeight(t, c, 2)

// check the txCount
resp, code := c.Request("GET", nil, "chain", "blocks", "1")
qt.Assert(t, code, qt.Equals, 200, qt.Commentf("response: %s", resp))

block := api.Block{}
err := json.Unmarshal(resp, &block)
qt.Assert(t, err, qt.IsNil)
qt.Assert(t, block.TxCount, qt.Equals, int64(1))
}

func runAPIElectionCostWithParams(t *testing.T,
electionParams electionprice.ElectionParameters,
startBlock uint32, initialBalance,
Expand Down

0 comments on commit bc896d2

Please sign in to comment.