diff --git a/x/exchange/keeper/grpc_query.go b/x/exchange/keeper/grpc_query.go index 77950c8b89..3d96d86378 100644 --- a/x/exchange/keeper/grpc_query.go +++ b/x/exchange/keeper/grpc_query.go @@ -3,6 +3,7 @@ package keeper import ( "context" "fmt" + "strings" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -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. diff --git a/x/exchange/keeper/keys.go b/x/exchange/keeper/keys.go index 68cea474cf..796344cfe7 100644 --- a/x/exchange/keeper/keys.go +++ b/x/exchange/keeper/keys.go @@ -58,9 +58,11 @@ import ( // Ask Orders: 0x02 | (8 bytes) => 0x00 | protobuf(AskOrder) // Bid Orders: 0x02 | (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 | (4 bytes) | (8 bytes) => // +// 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(
) (1 byte) |
| (8 bytes) => nil // @@ -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: +// - | (8 bytes) +// - (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) diff --git a/x/exchange/keeper/keys_test.go b/x/exchange/keeper/keys_test.go index fc3372c0e1..3154ef2fed 100644 --- a/x/exchange/keeper/keys_test.go +++ b/x/exchange/keeper/keys_test.go @@ -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