Skip to content

Commit

Permalink
[Feature] append blocking conditions for /txs query with --public
Browse files Browse the repository at this point in the history
… flag on LCD (#437)

* block querying genesis txs from public node
* update txs query blocking condition for public node
  • Loading branch information
yys authored Jan 27, 2021
1 parent 50b22b3 commit 32a5c53
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 85 deletions.
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)
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
}

0 comments on commit 32a5c53

Please sign in to comment.