diff --git a/api/chain.go b/api/chain.go index d10865664..9dbc090cd 100644 --- a/api/chain.go +++ b/api/chain.go @@ -116,7 +116,7 @@ func (a *API) enableChainHandlers() error { "/chain/transactions/{height}/{index}", "GET", apirest.MethodAccessTypePublic, - a.chainTxHandler, + a.chainTxByHeightAndIndexHandler, ); err != nil { return err } @@ -136,6 +136,14 @@ func (a *API) enableChainHandlers() error { ); err != nil { return err } + if err := a.Endpoint.RegisterMethod( + "/chain/transactions/{hash}", + "GET", + apirest.MethodAccessTypePublic, + a.chainTxByHashHandler, + ); err != nil { + return err + } if err := a.Endpoint.RegisterMethod( "/chain/transactions/page/{page}", "GET", @@ -639,7 +647,7 @@ func (a *API) chainTxCostHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext // @Success 204 "See [errors](vocdoni-api#errors) section" // @Router /chain/transactions/reference/{hash} [get] func (a *API) chainTxRefByHashHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error { - hash, err := hex.DecodeString(util.TrimHex(ctx.URLParam("hash"))) + hash, err := hex.DecodeString(util.TrimHex(ctx.URLParam(ParamHash))) if err != nil { return err } @@ -658,7 +666,7 @@ func (a *API) chainTxRefByHashHandler(_ *apirest.APIdata, ctx *httprouter.HTTPCo return ctx.Send(data, apirest.HTTPstatusOK) } -// chainTxHandler +// chainTxByHeightAndIndexHandler // // @Summary Transaction by block height and index // @Description Get transaction full information by block height and index. It returns JSON transaction protobuf encoded. Depending of transaction type will return different types of objects. Current transaction types can be found calling `/chain/transactions/cost` @@ -670,7 +678,7 @@ func (a *API) chainTxRefByHashHandler(_ *apirest.APIdata, ctx *httprouter.HTTPCo // @Success 200 {object} GenericTransactionWithInfo // @Success 204 "See [errors](vocdoni-api#errors) section" // @Router /chain/transactions/{height}/{index} [get] -func (a *API) chainTxHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error { +func (a *API) chainTxByHeightAndIndexHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error { height, err := strconv.ParseInt(ctx.URLParam(ParamHeight), 10, 64) if err != nil { return err @@ -697,6 +705,40 @@ func (a *API) chainTxHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) er return ctx.Send(data, apirest.HTTPstatusOK) } +// chainTxByHashHandler +// +// @Summary Transaction by hash +// @Description Get transaction full information by hash. It returns JSON transaction protobuf encoded. Depending of transaction type will return different types of objects. Current transaction types can be found calling `/chain/transactions/cost` +// @Tags Chain +// @Accept json +// @Produce json +// @Param hash path string true "Transaction hash" +// @Success 200 {object} GenericTransactionWithInfo +// @Success 204 "See [errors](vocdoni-api#errors) section" +// @Router /chain/transactions/{hash} [get] +func (a *API) chainTxByHashHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error { + hash, err := hex.DecodeString(util.TrimHex(ctx.URLParam(ParamHash))) + if err != nil { + return ErrCantParseHexString.WithErr(err) + } + ref, err := a.indexer.GetTransactionByHash(hash) + if err != nil { + if errors.Is(err, indexer.ErrTransactionNotFound) { + return ErrTransactionNotFound + } + return ErrVochainGetTxFailed.WithErr(err) + } + tx := &GenericTransactionWithInfo{ + TxContent: protoTxAsJSON(ref.RawTx), + TxInfo: ref, + } + data, err := json.Marshal(tx) + if err != nil { + return err + } + return ctx.Send(data, apirest.HTTPstatusOK) +} + // chainTxListHandler // // @Summary List transactions diff --git a/vochain/indexer/transaction.go b/vochain/indexer/transaction.go index 31beb4465..41a6a1818 100644 --- a/vochain/indexer/transaction.go +++ b/vochain/indexer/transaction.go @@ -42,6 +42,18 @@ func (idx *Indexer) GetTxMetadataByHash(hash types.HexBytes) (*indexertypes.Tran return indexertypes.TransactionMetadataFromDB(&sqlTxRef), nil } +// GetTransactionByHash fetches the full tx for the given tx hash +func (idx *Indexer) GetTransactionByHash(hash types.HexBytes) (*indexertypes.Transaction, error) { + sqlTxRef, err := idx.readOnlyQuery.GetTransactionByHash(context.TODO(), hash) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrTransactionNotFound + } + return nil, fmt.Errorf("tx hash %x not found: %v", hash, err) + } + return indexertypes.TransactionFromDB(&sqlTxRef), nil +} + // GetTransactionByHeightAndIndex fetches the full tx for the given tx height and block tx index func (idx *Indexer) GetTransactionByHeightAndIndex(blockHeight, blockIndex int64) (*indexertypes.Transaction, error) { sqlTxRef, err := idx.readOnlyQuery.GetTransactionByHeightAndIndex(context.TODO(), indexerdb.GetTransactionByHeightAndIndexParams{