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

[Feature] append blocking conditions for /txs query with --public flag on LCD #437

Merged
merged 2 commits into from
Jan 27, 2021
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
2 changes: 1 addition & 1 deletion client/lcd/statik/statik.go

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions client/lcd/swagger-ui/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ paths:
- in: query
name: message.sender
type: string
description: "transaction tags with sender: 'GET /txs?message.action=send&message.sender=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv'"
description: "transaction events with sender: 'GET /txs?message.action=send&message.sender=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv'"
x-example: "terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv"
- in: query
name: page
Expand All @@ -277,7 +277,15 @@ paths:
x-example: 1
- in: query
name: tx.height
description: "Tx height range option. It should be integer or query string(GTE, GT, LTE, LT): `GET /txs?tx.height=\"GTE3,LT10\"` or `GET /txs?tx.height=100`\nIn case using query string, the range can not exceed 100"
description: "transaction events with height: 'GET /txs?tx.height=10000'"
x-example: 100
- in: query
name: tx.minheight
description: "transaction events with height range option: 'GET /txs?tx.minheight=10000'"
x-example: 100
- in: query
name: tx.maxheight
description: "transaction events with height range option: 'GET /txs?tx.maxheight=10000'"
x-example: 100
responses:
200:
Expand Down
126 changes: 44 additions & 82 deletions x/auth/client/rest/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"net/http"
"strconv"
"strings"

"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -17,7 +16,10 @@ import (
"github.com/terra-project/core/client/lcd"
)

// QueryTxsHandlerFn implements a REST handler that searches for transactions.
// TxQueryMaxHeightRange maximum allowed height range for /txs query
const TxQueryMaxHeightRange = 100

// QueryTxsRequestHandlerFn implements a REST handler that searches for transactions.
// Genesis transactions are returned if the height parameter is set to zero,
// otherwise the transactions are searched for by events.
func QueryTxsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
Expand All @@ -31,40 +33,32 @@ func QueryTxsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return
}

// Check public node option
isPublicOpen := viper.GetBool(lcd.FlagPublic)

// if the height query param is set to zero, query for genesis transactions
heightStr := r.FormValue("height")
if heightStr != "" {
if height, err := strconv.ParseInt(heightStr, 10, 64); err == nil && height == 0 {
genutilrest.QueryGenesisTxs(cliCtx, w)
if isPublicOpen {
rest.WriteErrorResponse(
w, http.StatusBadRequest,
fmt.Sprintf("query genesis txs is not allowed for the public node"),
)
} else {
genutilrest.QueryGenesisTxs(cliCtx, w)
}

return
}
}

txHeightStr := r.FormValue(types.TxHeightKey)

// enforce tx.height query parameter
isPublicOpen := viper.GetBool(lcd.FlagPublic)
if isPublicOpen {
if txHeightStr == "" {
if err := validateTxHeightRange(r); err != nil {
rest.WriteErrorResponse(
w, http.StatusBadRequest,
fmt.Sprint("it is not allowed to query txs without tx.height option. please refer {URL}/swagger-ui"),
)
return
}
}

// parse tx.height query parameter
var txHeightEvents []string
if _, err := strconv.ParseInt(txHeightStr, 10, 64); len(txHeightStr) != 0 && err != nil {
// remove query parameter to prevent duplicated handling
delete(r.Form, types.TxHeightKey)

txHeightEvents, err = parseHeightRange(txHeightStr)
if err != nil {
rest.WriteErrorResponse(
w, http.StatusBadRequest,
fmt.Sprintf("failed to parse %s: %s", types.TxHeightKey, err.Error()),
err.Error(),
)
return
}
Expand Down Expand Up @@ -92,11 +86,6 @@ func QueryTxsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return
}

//append tx height events
if txHeightEvents != nil && len(txHeightEvents) > 0 {
events = append(events, txHeightEvents...)
}

searchResult, err := utils.QueryTxsByEvents(cliCtx, events, page, limit)
hanjukim marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
Expand All @@ -107,63 +96,36 @@ func QueryTxsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
}
}

func parseHeightRange(txHeightStr string) (events []string, err error) {
queries := strings.Split(txHeightStr, ",")
if len(queries) != 2 {
err = fmt.Errorf("invalid tx.height options: %s", queries)
return
func validateTxHeightRange(r *http.Request) error {
txHeightStr := r.FormValue(types.TxHeightKey)
txMinHeightStr := r.FormValue(rest.TxMinHeightKey)
txMaxHeightStr := r.FormValue(rest.TxMaxHeightKey)

if txHeightStr == "" && (txMinHeightStr == "" || txMaxHeightStr == "") {
return fmt.Errorf(
"it is not allowed to query txs without %s or (%s && %s) options. please refer {URL}/swagger-ui",
types.TxHeightKey, rest.TxMaxHeightKey, rest.TxMinHeightKey)
}

var upperBound int64
var lowerBound int64
for _, query := range queries {
switch {
case strings.HasPrefix(query, "GTE"):
if h, err2 := strconv.ParseInt(query[3:], 10, 64); err2 == nil {
lowerBound = h
events = append(events, fmt.Sprintf("%s>=%d", types.TxHeightKey, h))
} else {
err = fmt.Errorf("failed to parse integer: %s", query[3:])
return
}
case strings.HasPrefix(query, "GT"):
if h, err2 := strconv.ParseInt(query[2:], 10, 64); err2 == nil {
lowerBound = h + 1
events = append(events, fmt.Sprintf("%s>%d", types.TxHeightKey, h))
} else {
err = fmt.Errorf("failed to parse integer: %s", query[2:])
return
}
case strings.HasPrefix(query, "LTE"):
if h, err2 := strconv.ParseInt(query[3:], 10, 64); err2 == nil {
upperBound = h
events = append(events, fmt.Sprintf("%s<=%d", types.TxHeightKey, h))
} else {
err = fmt.Errorf("failed to parse integer: %s", query[3:])
return
}
case strings.HasPrefix(query, "LT"):
if h, err2 := strconv.ParseInt(query[2:], 10, 64); err2 == nil {
upperBound = h - 1
events = append(events, fmt.Sprintf("%s<%d", types.TxHeightKey, h))
} else {
err = fmt.Errorf("failed to parse integer: %s", query[2:])
return
}
default:
err = fmt.Errorf("invalid operator: %s", query)
return
if txMinHeightStr != "" && txMaxHeightStr != "" {
txMinHeight, err := strconv.ParseInt(txMinHeightStr, 10, 64)
if err != nil {
return err
}

txMaxHeight, err := strconv.ParseInt(txMaxHeightStr, 10, 64)
if err != nil {
return err
}

if txMaxHeight < txMinHeight {
return fmt.Errorf("%s must be bigger than %s", rest.TxMaxHeightKey, rest.TxMinHeightKey)
}
}

boundDiff := upperBound - lowerBound
if boundDiff > 100 {
err = fmt.Errorf("max allowed tx.height range gap is 100: %d", boundDiff)
return
} else if boundDiff <= 0 {
err = fmt.Errorf("tx.height range gap should be positive: %d", boundDiff)
return
if txMaxHeight-txMinHeight > TxQueryMaxHeightRange {
return fmt.Errorf("tx height range must be smaller than %d", TxQueryMaxHeightRange)
}
}

return
return nil
}