Skip to content
This repository was archived by the owner on Mar 24, 2025. It is now read-only.

Commit b31522e

Browse files
author
Alex | Interchain Labs
authored
feat: deterministic query oracle (#893) (backport (#894)
1 parent 0cd5f1b commit b31522e

File tree

11 files changed

+2572
-125
lines changed

11 files changed

+2572
-125
lines changed

Diff for: api/slinky/oracle/v1/query.pulsar.go

+1,592-78
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: api/slinky/oracle/v1/query_grpc.pb.go

+48-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: proto/slinky/oracle/v1/query.proto

+29
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ service Query {
3333
option (google.api.http).get =
3434
"/slinky/oracle/v1/get_currency_pair_mapping";
3535
}
36+
37+
// Get the mapping of currency pair ID <-> currency pair as a list. This is
38+
// useful for indexers that have access to the ID of a currency pair, but no
39+
// way to get the underlying currency pair from it.
40+
rpc GetCurrencyPairMappingList(GetCurrencyPairMappingListRequest)
41+
returns (GetCurrencyPairMappingListResponse) {
42+
option (google.api.http) = {
43+
get : "/connect/oracle/v2/get_currency_pair_mapping_list"
44+
additional_bindings : []
45+
};
46+
}
3647
}
3748

3849
message GetAllCurrencyPairsRequest {}
@@ -88,3 +99,21 @@ message GetCurrencyPairMappingResponse {
8899
map<uint64, slinky.types.v1.CurrencyPair> currency_pair_mapping = 1
89100
[ (gogoproto.nullable) = false ];
90101
}
102+
103+
// GetCurrencyPairMappingRequest is the GetCurrencyPairMapping request type.
104+
message GetCurrencyPairMappingListRequest {}
105+
106+
message CurrencyPairMapping {
107+
// ID is the unique identifier for this currency pair string.
108+
uint64 id = 1;
109+
// CurrencyPair is the human-readable representation of the currency pair.
110+
slinky.types.v1.CurrencyPair currency_pair = 2
111+
[ (gogoproto.nullable) = false ];
112+
}
113+
114+
// GetCurrencyPairMappingResponse is the GetCurrencyPairMapping response type.
115+
message GetCurrencyPairMappingListResponse {
116+
// mappings is a list of the id representing the currency pair
117+
// to the currency pair itself.
118+
repeated CurrencyPairMapping mappings = 1 [ (gogoproto.nullable) = false ];
119+
}

Diff for: tests/integration/slinky_setup.go

+75-1
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,81 @@ func QueryCurrencyPairs(chain *cosmos.CosmosChain) (*oracletypes.GetAllCurrencyP
269269
client := oracletypes.NewQueryClient(cc)
270270

271271
// query the currency pairs
272-
return client.GetAllCurrencyPairs(context.Background(), &oracletypes.GetAllCurrencyPairsRequest{})
272+
resp, err := client.GetAllCurrencyPairs(context.Background(), &oracletypes.GetAllCurrencyPairsRequest{})
273+
274+
// check that there is a correspondence between mappings and the raw response
275+
mappingResp, err := QueryCurrencyPairMappings(chain)
276+
if err != nil {
277+
return nil, err
278+
}
279+
280+
if len(resp.CurrencyPairs) != len(mappingResp.CurrencyPairMapping) {
281+
return nil, fmt.Errorf("list and map responses should be the same length: got %d list, %d map",
282+
len(resp.CurrencyPairs),
283+
len(mappingResp.CurrencyPairMapping),
284+
)
285+
}
286+
for _, v := range mappingResp.CurrencyPairMapping {
287+
found := false
288+
for _, cp := range resp.CurrencyPairs {
289+
if v.Equal(cp) {
290+
found = true
291+
}
292+
}
293+
294+
if !found {
295+
return nil, fmt.Errorf("currency pair %v was found in mapping response but not in currency pair list", v)
296+
}
297+
}
298+
299+
return resp, err
300+
}
301+
302+
// QueryCurrencyPairMappings queries the chain for the given currency pair mappings
303+
func QueryCurrencyPairMappings(chain *cosmos.CosmosChain) (*oracletypes.GetCurrencyPairMappingResponse, error) {
304+
// get grpc address
305+
grpcAddr := chain.GetHostGRPCAddress()
306+
307+
// create the client
308+
cc, err := grpc.Dial(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
309+
if err != nil {
310+
return nil, err
311+
}
312+
defer cc.Close()
313+
314+
// create the oracle client
315+
client := oracletypes.NewQueryClient(cc)
316+
317+
// query the currency pairs map
318+
mapRes, err := client.GetCurrencyPairMapping(context.Background(), &oracletypes.GetCurrencyPairMappingRequest{})
319+
if err != nil {
320+
return nil, err
321+
}
322+
323+
// query the currency pairs list
324+
listRes, err := client.GetCurrencyPairMappingList(context.Background(), &oracletypes.GetCurrencyPairMappingListRequest{})
325+
if err != nil {
326+
return nil, err
327+
}
328+
329+
if len(listRes.Mappings) != len(mapRes.CurrencyPairMapping) {
330+
return nil, fmt.Errorf("map and list responses should be the same length: got %d list, %d map",
331+
len(listRes.Mappings),
332+
len(mapRes.CurrencyPairMapping),
333+
)
334+
}
335+
for _, m := range listRes.Mappings {
336+
cp, found := mapRes.CurrencyPairMapping[m.Id]
337+
if !found {
338+
return nil, fmt.Errorf("mapping for %d not found", m.Id)
339+
}
340+
341+
if !m.CurrencyPair.Equal(cp) {
342+
return nil, fmt.Errorf("market %s is not equal to %s", m.CurrencyPair.String(), cp.String())
343+
}
344+
}
345+
346+
return mapRes, nil
273347
}
274348

275349
// QueryCurrencyPair queries the price for the given currency-pair given a desired height to query from

Diff for: x/marketmap/keeper/query.go

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ func NewQueryServer(k *Keeper) types.QueryServer {
1919
}
2020

2121
// MarketMap returns the full MarketMap and associated information stored in the x/marketmap module.
22+
//
23+
// NOTE: the map type returned by this query is NOT SAFE. Use Markets instead for a safe value.
2224
func (q queryServerImpl) MarketMap(goCtx context.Context, req *types.MarketMapRequest) (*types.MarketMapResponse, error) {
2325
if req == nil {
2426
return nil, fmt.Errorf("request cannot be nil")

Diff for: x/oracle/keeper/grpc_query.go

+13
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ func (q queryServer) GetPrices(goCtx context.Context, req *types.GetPricesReques
124124
}, nil
125125
}
126126

127+
// GetCurrencyPairMapping returns a map of ID -> CurrencyPair.
128+
//
129+
// NOTE: the map type returned by this query is NOT SAFE. Use GetCurrencyPairMappingList instead for a safe value.
127130
func (q queryServer) GetCurrencyPairMapping(ctx context.Context, _ *types.GetCurrencyPairMappingRequest) (*types.GetCurrencyPairMappingResponse, error) {
128131
sdkCtx := sdk.UnwrapSDKContext(ctx)
129132
pairs, err := q.k.GetCurrencyPairMapping(sdkCtx)
@@ -132,3 +135,13 @@ func (q queryServer) GetCurrencyPairMapping(ctx context.Context, _ *types.GetCur
132135
}
133136
return &types.GetCurrencyPairMappingResponse{CurrencyPairMapping: pairs}, nil
134137
}
138+
139+
func (q queryServer) GetCurrencyPairMappingList(ctx context.Context, _ *types.GetCurrencyPairMappingListRequest) (*types.GetCurrencyPairMappingListResponse, error) {
140+
sdkCtx := sdk.UnwrapSDKContext(ctx)
141+
pairs, err := q.k.GetCurrencyPairMappingList(sdkCtx)
142+
if err != nil {
143+
return nil, err
144+
}
145+
146+
return &types.GetCurrencyPairMappingListResponse{Mappings: pairs}, nil
147+
}

Diff for: x/oracle/keeper/grpc_query_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,43 @@ func (s *KeeperTestSuite) TestGetCurrencyPairMappingGRPC() {
233233
}
234234
})
235235
}
236+
237+
func (s *KeeperTestSuite) TestGetCurrencyPairMappingListGRPC() {
238+
cp1 := slinkytypes.CurrencyPair{Base: "TEST", Quote: "COIN1"}
239+
cp2 := slinkytypes.CurrencyPair{Base: "TEST", Quote: "COIN2"}
240+
cp3 := slinkytypes.CurrencyPair{Base: "TEST", Quote: "COIN3"}
241+
242+
qs := keeper.NewQueryServer(s.oracleKeeper)
243+
// test that after CurrencyPairs are registered, all of them are returned from the query
244+
s.Run("after CurrencyPairs are registered, all of them are returned from the query", func() {
245+
currencyPairs := []slinkytypes.CurrencyPair{
246+
cp1,
247+
cp2,
248+
cp3,
249+
}
250+
for _, cp := range currencyPairs {
251+
s.Require().NoError(s.oracleKeeper.CreateCurrencyPair(s.ctx, cp))
252+
}
253+
254+
// manually insert a new CurrencyPair as well
255+
s.Require().NoError(s.oracleKeeper.SetPriceForCurrencyPair(s.ctx, cp1, types.QuotePrice{Price: sdkmath.NewInt(100)}))
256+
257+
// query for pairs
258+
res, err := qs.GetCurrencyPairMappingList(s.ctx, nil)
259+
s.Require().Nil(err)
260+
s.Require().Equal([]types.CurrencyPairMapping{
261+
{
262+
Id: 0,
263+
CurrencyPair: cp1,
264+
},
265+
{
266+
Id: 1,
267+
CurrencyPair: cp2,
268+
},
269+
{
270+
Id: 2,
271+
CurrencyPair: cp3,
272+
},
273+
}, res.Mappings)
274+
})
275+
}

Diff for: x/oracle/keeper/keeper.go

+18
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ func (k *Keeper) GetAllCurrencyPairs(ctx sdk.Context) []slinkytypes.CurrencyPair
287287
}
288288

289289
// GetCurrencyPairMapping returns a CurrencyPair mapping by ID that have currently been stored to state.
290+
// NOTE: this map[] type should not be used by on-chain code.
290291
func (k *Keeper) GetCurrencyPairMapping(ctx sdk.Context) (map[uint64]slinkytypes.CurrencyPair, error) {
291292
numPairs, err := k.numCPs.Get(ctx)
292293
if err != nil {
@@ -301,6 +302,23 @@ func (k *Keeper) GetCurrencyPairMapping(ctx sdk.Context) (map[uint64]slinkytypes
301302
return pairs, nil
302303
}
303304

305+
// GetCurrencyPairMappingList returns a CurrencyPair mapping by ID that have currently been stored to state as a list.
306+
func (k *Keeper) GetCurrencyPairMappingList(ctx sdk.Context) ([]types.CurrencyPairMapping, error) {
307+
pairs := make([]types.CurrencyPairMapping, 0)
308+
// aggregate CurrencyPairs stored under KeyPrefixNonce
309+
err := k.IterateCurrencyPairs(ctx, func(cp slinkytypes.CurrencyPair, cps types.CurrencyPairState) {
310+
pairs = append(pairs, types.CurrencyPairMapping{
311+
Id: cps.GetId(),
312+
CurrencyPair: cp,
313+
})
314+
})
315+
if err != nil {
316+
return nil, err
317+
}
318+
319+
return pairs, nil
320+
}
321+
304322
// IterateCurrencyPairs iterates over all CurrencyPairs in the store, and executes a callback for each CurrencyPair.
305323
func (k *Keeper) IterateCurrencyPairs(ctx sdk.Context, cb func(cp slinkytypes.CurrencyPair, cps types.CurrencyPairState)) error {
306324
it, err := k.currencyPairs.Iterate(ctx, nil)

0 commit comments

Comments
 (0)