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

Improve bound and cache update checks #343

Merged
merged 9 commits into from
Jan 28, 2025
4 changes: 2 additions & 2 deletions cmd/stellar-rpc/internal/db/cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ var (
//nolint:gochecknoglobals
MaxCursor = Cursor{
Ledger: math.MaxInt32,
Tx: math.MaxInt32,
Op: math.MaxInt32,
Tx: toid.TransactionMask,
Op: toid.OperationMask,
Event: math.MaxUint32,
}
)
7 changes: 7 additions & 0 deletions cmd/stellar-rpc/internal/db/cursor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,10 @@ func TestCursorCmp(t *testing.T) {
}
}
}

func TestMaxCursor(t *testing.T) {
str := MaxCursor.String()
cursor, err := ParseCursor(str)
require.NoError(t, err)
assert.Equal(t, MaxCursor, cursor)
}
4 changes: 4 additions & 0 deletions cmd/stellar-rpc/internal/db/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package db
import (
"context"
"database/sql"
"errors"
"fmt"

sq "github.com/Masterminds/squirrel"
Expand Down Expand Up @@ -63,6 +64,9 @@ func (l ledgerReaderTx) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.
func (l ledgerReaderTx) BatchGetLedgers(ctx context.Context, sequence uint32,
batchSize uint,
) ([]xdr.LedgerCloseMeta, error) {
if batchSize < 1 {
return nil, errors.New("batch size must be greater than zero")
}
sql := sq.Select("meta").
From(ledgerCloseMetaTableName).
Where(sq.And{
Expand Down
36 changes: 21 additions & 15 deletions cmd/stellar-rpc/internal/methods/get_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"math"
"strings"
"time"

Expand Down Expand Up @@ -119,19 +118,6 @@ func (g *GetEventsRequest) Valid(maxLimit uint) error {
return err
}

// Validate the paging limit (if it exists)
if g.Pagination != nil && g.Pagination.Cursor != nil {
if g.StartLedger != 0 || g.EndLedger != 0 {
return errors.New("ledger ranges and cursor cannot both be set")
}
} else if g.StartLedger <= 0 {
return errors.New("startLedger must be positive")
}

if g.Pagination != nil && g.Pagination.Limit > maxLimit {
return fmt.Errorf("limit must not exceed %d", maxLimit)
}

// Validate filters
if len(g.Filters) > maxFiltersLimit {
return errors.New("maximum 5 filters per request")
Expand All @@ -142,6 +128,24 @@ func (g *GetEventsRequest) Valid(maxLimit uint) error {
}
}

if g.Pagination != nil {
if g.Pagination.Cursor != nil && (g.StartLedger != 0 || g.EndLedger != 0) {
return errors.New("ledger ranges and cursor cannot both be set")
}
if g.Pagination.Limit > maxLimit {
return fmt.Errorf("limit must not exceed %d", maxLimit)
}
return nil
}

// Pagination not enabled
if g.StartLedger <= 0 {
return errors.New("startLedger must be positive")
}
if g.EndLedger > 0 && g.EndLedger < g.StartLedger {
return errors.New("startLedger must be >= endLedger")
}

return nil
}

Expand Down Expand Up @@ -524,7 +528,9 @@ func (h eventsRPCHandler) getEvents(ctx context.Context, request GetEventsReques
// cursor represents end of the search window if events does not reach limit
// here endLedger is always exclusive when fetching events
// so search window is max Cursor value with endLedger - 1
cursor = db.Cursor{Ledger: endLedger - 1, Tx: math.MaxUint32, Event: math.MaxUint32 - 1}.String()
maxCursor := db.MaxCursor
maxCursor.Ledger = endLedger - 1
cursor = maxCursor.String()
}

return GetEventsResponse{
Expand Down
33 changes: 20 additions & 13 deletions cmd/stellar-rpc/internal/methods/get_events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"math"
"path"
"strconv"
"strings"
Expand Down Expand Up @@ -656,8 +655,10 @@ func TestGetEvents(t *testing.T) {
TransactionHash: ledgerCloseMeta.TransactionHash(i).HexString(),
})
}
cursor := db.Cursor{Ledger: 1, Tx: math.MaxUint32, Event: math.MaxUint32 - 1}.String()
assert.Equal(t, GetEventsResponse{expected, 1, cursor}, results)
cursor := db.MaxCursor
cursor.Ledger = 1
cursorStr := cursor.String()
assert.Equal(t, GetEventsResponse{expected, 1, cursorStr}, results)
})

t.Run("filtering by contract id", func(t *testing.T) {
Expand Down Expand Up @@ -803,9 +804,11 @@ func TestGetEvents(t *testing.T) {
TransactionHash: ledgerCloseMeta.TransactionHash(4).HexString(),
},
}
cursor := db.Cursor{Ledger: 1, Tx: math.MaxUint32, Event: math.MaxUint32 - 1}.String()

assert.Equal(t, GetEventsResponse{expected, 1, cursor}, results)
cursor := db.MaxCursor
cursor.Ledger = 1
cursorStr := cursor.String()
assert.Equal(t, GetEventsResponse{expected, 1, cursorStr}, results)

results, err = handler.getEvents(ctx, GetEventsRequest{
StartLedger: 1,
Expand Down Expand Up @@ -839,7 +842,7 @@ func TestGetEvents(t *testing.T) {

expected[0].ValueJSON = valueJs
expected[0].TopicJSON = topicsJs
require.Equal(t, GetEventsResponse{expected, 1, cursor}, results)
require.Equal(t, GetEventsResponse{expected, 1, cursorStr}, results)
})

t.Run("filtering by both contract id and topic", func(t *testing.T) {
Expand Down Expand Up @@ -950,9 +953,10 @@ func TestGetEvents(t *testing.T) {
TransactionHash: ledgerCloseMeta.TransactionHash(3).HexString(),
},
}
cursor := db.Cursor{Ledger: 1, Tx: math.MaxUint32, Event: math.MaxUint32 - 1}.String()

assert.Equal(t, GetEventsResponse{expected, 1, cursor}, results)
cursor := db.MaxCursor
cursor.Ledger = 1
cursorStr := cursor.String()
assert.Equal(t, GetEventsResponse{expected, 1, cursorStr}, results)
})

t.Run("filtering by event type", func(t *testing.T) {
Expand Down Expand Up @@ -1027,9 +1031,10 @@ func TestGetEvents(t *testing.T) {
TransactionHash: ledgerCloseMeta.TransactionHash(0).HexString(),
},
}
cursor := db.Cursor{Ledger: 1, Tx: math.MaxUint32, Event: math.MaxUint32 - 1}.String()

assert.Equal(t, GetEventsResponse{expected, 1, cursor}, results)
cursor := db.MaxCursor
cursor.Ledger = 1
cursorStr := cursor.String()
assert.Equal(t, GetEventsResponse{expected, 1, cursorStr}, results)
})

t.Run("with limit", func(t *testing.T) {
Expand Down Expand Up @@ -1218,7 +1223,9 @@ func TestGetEvents(t *testing.T) {

// Note: endLedger is always exclusive when fetching events
// so search window is always max Cursor value with endLedger - 1
cursor = db.Cursor{Ledger: uint32(endLedger - 1), Tx: math.MaxUint32, Event: math.MaxUint32 - 1}.String()
rawCursor := db.MaxCursor
rawCursor.Ledger = uint32(endLedger - 1)
cursor = rawCursor.String()
assert.Equal(t, GetEventsResponse{[]EventInfo{}, 5, cursor}, results)
})
}
Expand Down
22 changes: 13 additions & 9 deletions cmd/stellar-rpc/internal/methods/get_transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,27 @@ type GetTransactionsRequest struct {

// isValid checks the validity of the request parameters.
func (req GetTransactionsRequest) isValid(maxLimit uint, ledgerRange ledgerbucketwindow.LedgerRange) error {
if req.Pagination != nil && req.Pagination.Cursor != "" {
if req.StartLedger != 0 {
if err := IsValidFormat(req.Format); err != nil {
return err
}
if req.Pagination != nil {
if req.Pagination.Cursor != "" && req.StartLedger != 0 {
return errors.New("startLedger and cursor cannot both be set")
}
} else if req.StartLedger < ledgerRange.FirstLedger.Sequence || req.StartLedger > ledgerRange.LastLedger.Sequence {
if req.Pagination.Limit > maxLimit {
return fmt.Errorf("limit must not exceed %d", maxLimit)
}
return nil
}
// Pagination is not enabled
if req.StartLedger < ledgerRange.FirstLedger.Sequence || req.StartLedger > ledgerRange.LastLedger.Sequence {
return fmt.Errorf(
"start ledger must be between the oldest ledger: %d and the latest ledger: %d for this rpc instance",
ledgerRange.FirstLedger.Sequence,
ledgerRange.LastLedger.Sequence,
)
}

if req.Pagination != nil && req.Pagination.Limit > maxLimit {
return fmt.Errorf("limit must not exceed %d", maxLimit)
}

return IsValidFormat(req.Format)
return nil
}

type TransactionDetails struct {
Expand Down
Loading