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

Commit bbd36fe

Browse files
author
Alex | Interchain Labs
authored
feat: deterministic query oracle (#893)
1 parent d2dfc66 commit bbd36fe

File tree

11 files changed

+2573
-127
lines changed

11 files changed

+2573
-127
lines changed

Diff for: api/connect/oracle/v2/query.pulsar.go

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

Diff for: api/connect/oracle/v2/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/connect/oracle/v2/query.proto

+29
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ service Query {
4141
additional_bindings : []
4242
};
4343
}
44+
45+
// Get the mapping of currency pair ID <-> currency pair as a list. This is
46+
// useful for indexers that have access to the ID of a currency pair, but no
47+
// way to get the underlying currency pair from it.
48+
rpc GetCurrencyPairMappingList(GetCurrencyPairMappingListRequest)
49+
returns (GetCurrencyPairMappingListResponse) {
50+
option (google.api.http) = {
51+
get : "/connect/oracle/v2/get_currency_pair_mapping_list"
52+
additional_bindings : []
53+
};
54+
}
4455
}
4556

4657
message GetAllCurrencyPairsRequest {}
@@ -94,3 +105,21 @@ message GetCurrencyPairMappingResponse {
94105
map<uint64, connect.types.v2.CurrencyPair> currency_pair_mapping = 1
95106
[ (gogoproto.nullable) = false ];
96107
}
108+
109+
// GetCurrencyPairMappingRequest is the GetCurrencyPairMapping request type.
110+
message GetCurrencyPairMappingListRequest {}
111+
112+
message CurrencyPairMapping {
113+
// ID is the unique identifier for this currency pair string.
114+
uint64 id = 1;
115+
// CurrencyPair is the human-readable representation of the currency pair.
116+
connect.types.v2.CurrencyPair currency_pair = 2
117+
[ (gogoproto.nullable) = false ];
118+
}
119+
120+
// GetCurrencyPairMappingResponse is the GetCurrencyPairMapping response type.
121+
message GetCurrencyPairMappingListResponse {
122+
// mappings is a list of the id representing the currency pair
123+
// to the currency pair itself.
124+
repeated CurrencyPairMapping mappings = 1 [ (gogoproto.nullable) = false ];
125+
}

Diff for: tests/integration/connect_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

+12
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,22 @@ func (q queryServer) GetPrices(ctx context.Context, req *types.GetPricesRequest)
122122
}, nil
123123
}
124124

125+
// GetCurrencyPairMapping returns a map of ID -> CurrencyPair.
126+
//
127+
// NOTE: the map type returned by this query is NOT SAFE. Use GetCurrencyPairMappingList instead for a safe value.
125128
func (q queryServer) GetCurrencyPairMapping(ctx context.Context, _ *types.GetCurrencyPairMappingRequest) (*types.GetCurrencyPairMappingResponse, error) {
126129
pairs, err := q.k.GetCurrencyPairMapping(ctx)
127130
if err != nil {
128131
return nil, err
129132
}
130133
return &types.GetCurrencyPairMappingResponse{CurrencyPairMapping: pairs}, nil
131134
}
135+
136+
func (q queryServer) GetCurrencyPairMappingList(ctx context.Context, _ *types.GetCurrencyPairMappingListRequest) (*types.GetCurrencyPairMappingListResponse, error) {
137+
pairs, err := q.k.GetCurrencyPairMappingList(ctx)
138+
if err != nil {
139+
return nil, err
140+
}
141+
142+
return &types.GetCurrencyPairMappingListResponse{Mappings: pairs}, nil
143+
}

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 := connecttypes.CurrencyPair{Base: "TEST", Quote: "COIN1"}
239+
cp2 := connecttypes.CurrencyPair{Base: "TEST", Quote: "COIN2"}
240+
cp3 := connecttypes.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 := []connecttypes.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
@@ -288,6 +288,7 @@ func (k *Keeper) GetAllCurrencyPairs(ctx context.Context) []connecttypes.Currenc
288288
}
289289

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

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

0 commit comments

Comments
 (0)