Skip to content

Commit

Permalink
[1658]: Implement QueryGetAssetOrders and QueryGetAllOrders.
Browse files Browse the repository at this point in the history
  • Loading branch information
SpicyLemon committed Sep 29, 2023
1 parent c6ef636 commit 2741766
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 4 deletions.
81 changes: 77 additions & 4 deletions x/exchange/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"context"
"fmt"
"strings"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -166,14 +167,86 @@ func (k QueryServer) QueryGetOwnerOrders(goCtx context.Context, req *exchange.Qu

// QueryGetAssetOrders looks up the orders for a specific asset denom.
func (k QueryServer) QueryGetAssetOrders(goCtx context.Context, req *exchange.QueryGetAssetOrdersRequest) (*exchange.QueryGetAssetOrdersResponse, error) {
// TODO[1658]: Implement QueryGetAssetOrders query
panic("not implemented")
if req == nil || len(req.Asset) == 0 {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

var pre []byte
switch strings.ToLower(req.OrderType) {
case "":
pre = GetIndexKeyPrefixAssetToOrder(req.Asset)
case exchange.OrderTypeAsk:
pre = GetIndexKeyPrefixAssetToOrderAsks(req.Asset)
case exchange.OrderTypeBid:
pre = GetIndexKeyPrefixAssetToOrderBids(req.Asset)
default:
return nil, status.Errorf(codes.InvalidArgument, "unknown order type %q", req.OrderType)
}

ctx := sdk.UnwrapSDKContext(goCtx)
store := prefix.NewStore(k.getStore(ctx), pre)
resp := &exchange.QueryGetAssetOrdersResponse{}
var pageErr error

resp.Pagination, pageErr = query.FilteredPaginate(store, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) {
// If we can't get the order id from the key, just pretend like it doesn't exist.
_, _, orderID, perr := ParseIndexKeyAssetToOrder(key)
if perr != nil {
return false, nil
}
if accumulate {
// Only add them to the result if we can read it.
// This might result in fewer results than the limit, but at least one bad entry won't block others.
order, oerr := k.parseOrderStoreValue(orderID, value)
if oerr != nil {
resp.Orders = append(resp.Orders, order)
}
}
return true, nil
})

if pageErr != nil {
return nil, status.Errorf(codes.InvalidArgument, "error iterating orders for asset %s: %v", req.Asset, pageErr)
}

return resp, nil
}

// QueryGetAllOrders gets all orders in the exchange module.
func (k QueryServer) QueryGetAllOrders(goCtx context.Context, req *exchange.QueryGetAllOrdersRequest) (*exchange.QueryGetAllOrdersResponse, error) {
// TODO[1658]: Implement QueryGetAllOrders query
panic("not implemented")
var pagination *query.PageRequest
if req != nil {
pagination = req.Pagination
}

ctx := sdk.UnwrapSDKContext(goCtx)
pre := GetKeyPrefixOrder()
store := prefix.NewStore(k.getStore(ctx), pre)
resp := &exchange.QueryGetAllOrdersResponse{}
var pageErr error

resp.Pagination, pageErr = query.FilteredPaginate(store, pagination, func(key []byte, value []byte, accumulate bool) (bool, error) {
// If we can't get the order id from the key, just pretend like it doesn't exist.
orderID, ok := ParseKeyOrder(key)
if !ok {
return false, nil
}
if accumulate {
// Only add them to the result if we can read it.
// This might result in fewer results than the limit, but at least one bad entry won't block others.
order, oerr := k.parseOrderStoreValue(orderID, value)
if oerr != nil {
resp.Orders = append(resp.Orders, order)
}
}
return true, nil
})

if pageErr != nil {
return nil, status.Errorf(codes.InvalidArgument, "error iterating all orders: %v", pageErr)
}

return resp, nil
}

// QueryMarketInfo returns the information/details about a market.
Expand Down
18 changes: 18 additions & 0 deletions x/exchange/keeper/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ import (
// Ask Orders: 0x02 | <order_id> (8 bytes) => 0x00 | protobuf(AskOrder)
// Bid Orders: 0x02 | <order_id> (8 bytes) => 0x01 | protobuf(BidOrder)
//
// TODO[1658]: Refactor the market to order index to have the order type byte before the order id.
// A market to order index is maintained with the following format:
// 0x03 | <market_id> (4 bytes) | <order_id> (8 bytes) => <order type byte>
//
// TODO[1658]: Refactor the address to order index to have the order type byte before the order id.
// An address to order index is maintained with the following format:
// 0x04 | len(<address>) (1 byte) | <address> | <order_id> (8 bytes) => nil
//
Expand Down Expand Up @@ -542,6 +544,22 @@ func MakeKeyOrder(orderID uint64) []byte {
return rv
}

// ParseKeyOrder will extract the order id from the provided order key.
// The returned bool is whether parsing was successful (true = ok).
//
// The input can have the following formats:
// - <type byte> | <order id> (8 bytes)
// - <order id> (8 bytes)
func ParseKeyOrder(key []byte) (uint64, bool) {
if len(key) < 8 || len(key) > 9 {
return 0, false
}
if len(key) == 9 && key[0] != OrderKeyTypeAsk && key[0] != OrderKeyTypeBid {
return 0, false
}
return uint64FromBz(key[len(key)-8:])
}

// indexPrefixMarketToOrder creates the prefix for the market to order prefix entries with some extra space for the rest.
func indexPrefixMarketToOrder(marketID uint32, extraCap int) []byte {
return prepKey(KeyTypeMarketToOrderIndex, uint32Bz(marketID), extraCap)
Expand Down
46 changes: 46 additions & 0 deletions x/exchange/keeper/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2822,6 +2822,52 @@ func TestMakeKeyOrder(t *testing.T) {
}
}

func TestParseKeyOrder(t *testing.T) {
tests := []struct {
name string
key []byte
expOrderID uint64
expOK bool
}{
{name: "nil key", key: nil, expOrderID: 0, expOK: false},
{name: "empty key", key: []byte{}, expOrderID: 0, expOK: false},
{name: "7 byte key", key: []byte{1, 2, 3, 4, 5, 6, 7}, expOrderID: 0, expOK: false},
{name: "10 byte key", key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, expOrderID: 0, expOK: false},
{name: "9 byte key unknown type", key: []byte{99, 1, 2, 3, 4, 5, 6, 7, 8}, expOrderID: 0, expOK: false},
{
name: "8 byte key",
key: []byte{1, 2, 3, 4, 5, 6, 7, 8},
expOrderID: 72_623_859_790_382_856,
expOK: true,
},
{
name: "9 byte key ask",
key: []byte{keeper.OrderKeyTypeAsk, 1, 2, 3, 4, 5, 6, 7, 8},
expOrderID: 72_623_859_790_382_856,
expOK: true,
},
{
name: "9 byte key bid",
key: []byte{keeper.OrderKeyTypeBid, 1, 2, 3, 4, 5, 6, 7, 8},
expOrderID: 72_623_859_790_382_856,
expOK: true,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var orderID uint64
var ok bool
testFunc := func() {
orderID, ok = keeper.ParseKeyOrder(tc.key)
}
require.NotPanics(t, testFunc, "ParseKeyOrder")
assert.Equal(t, tc.expOK, ok, "ParseKeyOrder bool ok")
assert.Equal(t, tc.expOrderID, orderID, "ParseKeyOrder order ID")
})
}
}

func TestGetIndexKeyPrefixMarketToOrder(t *testing.T) {
tests := []struct {
name string
Expand Down

0 comments on commit 2741766

Please sign in to comment.