Skip to content

Commit 211a9f0

Browse files
authored
[DFI-561] Oracle mod: Init/Export genesis (#179)
* [DFI-561] Mod oracle: added import/export * [DFI-561] Mod oracle: added genesis validation * [DFI-561] Mod oracle: testing * [DFI-561] Mod oracle: additional tests * [DFI-561] Mod oracle: fixed imports * [DFI-561] Mod oracle: fixed test and validation * [DFI-561] Mod oracle: refactored addCurrentPrice method * [DFI-561] Mod oracle: fixed env variable for integ test
1 parent 7ea9487 commit 211a9f0

File tree

13 files changed

+456
-67
lines changed

13 files changed

+456
-67
lines changed

app/common_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,14 @@ func CheckSetGenesisDVM(t *testing.T, app *DnServiceApp, accs []*auth.BaseAccoun
477477

478478
// read VM genesis from file and add to genesisState
479479
{
480-
vmGenesisPath := os.ExpandEnv("${GOPATH}/src/github.com/dfinance/dnode/x/vm/internal/keeper/genesis_ws.json")
481-
stateBytes, err := ioutil.ReadFile(vmGenesisPath)
480+
vmGenesisPath := "${GOPATH}/src/github.com/dfinance/dnode/x/vm/internal/keeper/genesis_ws.json"
481+
482+
vmGenesisPathEnv := os.Getenv("VMWSPATH")
483+
if vmGenesisPathEnv != "" {
484+
vmGenesisPath = vmGenesisPathEnv
485+
}
486+
487+
stateBytes, err := ioutil.ReadFile(os.ExpandEnv(vmGenesisPath))
482488
require.NoError(t, err, "reading VM genesis file")
483489

484490
genesisState[vm.ModuleName] = stateBytes

x/oracle/alias.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type (
1616
Oracle = types.Oracle
1717
Oracles = types.Oracles
1818
CurrentPrice = types.CurrentPrice
19+
CurrentPrices = types.CurrentPrices
1920
PostedPrice = types.PostedPrice
2021
Keeper = keeper.Keeper
2122
MsgAddOracle = types.MsgAddOracle

x/oracle/internal/keeper/genesis.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package keeper
22

33
import (
44
"encoding/json"
5+
"fmt"
56

67
"github.com/dfinance/dnode/x/oracle/internal/types"
78

@@ -14,15 +15,33 @@ func (k Keeper) InitGenesis(ctx sdk.Context, data json.RawMessage) {
1415

1516
state := types.GenesisState{}
1617
k.cdc.MustUnmarshalJSON(data, &state)
18+
19+
if err := state.Validate(ctx.BlockTime()); err != nil {
20+
panic(err)
21+
}
22+
1723
k.SetParams(ctx, state.Params)
24+
25+
for _, cPrice := range state.CurrentPrices {
26+
if _, ok := k.GetAsset(ctx, cPrice.AssetCode); !ok {
27+
panic(fmt.Errorf("asset_code %s does not exist", cPrice.AssetCode))
28+
}
29+
k.addCurrentPrice(ctx, cPrice)
30+
}
1831
}
1932

2033
// ExportGenesis exports module genesis state using current params state.
2134
func (k Keeper) ExportGenesis(ctx sdk.Context) json.RawMessage {
2235
k.modulePerms.AutoCheck(types.PermRead)
2336

37+
currentPrices, err := k.GetCurrentPricesList(ctx)
38+
if err != nil {
39+
panic(err)
40+
}
41+
2442
state := types.GenesisState{
25-
Params: k.GetParams(ctx),
43+
Params: k.GetParams(ctx),
44+
CurrentPrices: currentPrices,
2645
}
2746

2847
return k.cdc.MustMarshalJSON(state)
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// +build unit
2+
3+
package keeper
4+
5+
import (
6+
"testing"
7+
"time"
8+
9+
sdk "github.com/cosmos/cosmos-sdk/types"
10+
"github.com/stretchr/testify/require"
11+
12+
dnTypes "github.com/dfinance/dnode/helpers/types"
13+
"github.com/dfinance/dnode/x/oracle/internal/types"
14+
)
15+
16+
func TestOracleKeeper_Genesis_Init(t *testing.T) {
17+
input := NewTestInput(t)
18+
19+
keeper := input.keeper
20+
ctx := input.ctx
21+
ctx = ctx.WithBlockTime(time.Now().Add(time.Hour))
22+
ctx = ctx.WithBlockHeight(3)
23+
cdc := input.cdc
24+
25+
// default genesis
26+
{
27+
keeper.InitGenesis(ctx, cdc.MustMarshalJSON(types.DefaultGenesisState()))
28+
cpList, err := keeper.GetCurrentPricesList(input.ctx)
29+
require.Nil(t, err)
30+
params := keeper.GetParams(input.ctx)
31+
require.Len(t, cpList, 0)
32+
defaultParams := types.DefaultParams()
33+
require.Equal(t, len(params.Assets), len(defaultParams.Assets))
34+
}
35+
36+
// prices asset code doesnt exist in assets
37+
{
38+
params := input.keeper.GetParams(ctx)
39+
params.Assets = types.Assets{
40+
types.NewAsset(dnTypes.AssetCode("eth_dfi"), types.Oracles{}, true),
41+
}
42+
43+
cpList := types.CurrentPrices{
44+
NewMockCurrentPrice("btc_dfi", 100),
45+
}
46+
47+
state := types.GenesisState{
48+
Params: params,
49+
CurrentPrices: cpList,
50+
}
51+
52+
defer func() {
53+
r := recover()
54+
require.NotNil(t, r)
55+
require.Contains(t, r.(error).Error(), "asset_code")
56+
require.Contains(t, r.(error).Error(), "does not exist")
57+
}()
58+
59+
keeper.InitGenesis(ctx, cdc.MustMarshalJSON(state))
60+
}
61+
62+
//import and export
63+
{
64+
params := input.keeper.GetParams(ctx)
65+
66+
oracles := types.Oracles{
67+
types.Oracle{
68+
Address: sdk.AccAddress{},
69+
},
70+
}
71+
72+
params.Assets = types.Assets{
73+
types.NewAsset(dnTypes.AssetCode("btc_dfi"), oracles, true),
74+
types.NewAsset(dnTypes.AssetCode("eth_dfi"), oracles, true),
75+
types.NewAsset(dnTypes.AssetCode("dfi_btc"), oracles, true),
76+
types.NewAsset(dnTypes.AssetCode("usdt_dfi"), oracles, true),
77+
}
78+
79+
cpList := types.CurrentPrices{
80+
NewMockCurrentPrice("btc_dfi", 100),
81+
NewMockCurrentPrice("eth_dfi", 200),
82+
NewMockCurrentPrice("dfi_btc", 300),
83+
NewMockCurrentPrice("usdt_dfi", 400),
84+
}
85+
86+
state := types.GenesisState{
87+
Params: params,
88+
CurrentPrices: cpList,
89+
}
90+
91+
// initialize and check current state with init values
92+
keeper.InitGenesis(ctx, cdc.MustMarshalJSON(state))
93+
94+
cpListFromKeeper, err := keeper.GetCurrentPricesList(ctx)
95+
require.Nil(t, err)
96+
require.Len(t, cpListFromKeeper, len(cpList))
97+
98+
paramsFromKeeper := keeper.GetParams(ctx)
99+
require.Equal(t, paramsFromKeeper.Assets, params.Assets)
100+
101+
// export and check exported values with initial
102+
var exportedState types.GenesisState
103+
cdc.MustUnmarshalJSON(keeper.ExportGenesis(ctx), &exportedState)
104+
105+
require.False(t, exportedState.IsEmpty())
106+
require.Equal(t, exportedState.Params.Assets, params.Assets)
107+
require.Equal(t, exportedState.Params.Nominees, params.Nominees)
108+
require.Equal(t, exportedState.Params.PostPrice, params.PostPrice)
109+
require.Equal(t, len(exportedState.CurrentPrices), len(state.CurrentPrices))
110+
111+
// checking all of items existing in the export
112+
sumPrices := sdk.NewIntFromUint64(0)
113+
for _, i := range exportedState.CurrentPrices {
114+
sumPrices = sumPrices.Add(i.Price)
115+
}
116+
117+
sumPricesInitial := sdk.NewIntFromUint64(0)
118+
for _, i := range state.CurrentPrices {
119+
sumPricesInitial = sumPricesInitial.Add(i.Price)
120+
}
121+
122+
require.Equal(t, sumPrices, sumPricesInitial)
123+
}
124+
}

x/oracle/internal/keeper/price.go

Lines changed: 79 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package keeper
22

33
import (
4+
"fmt"
45
"sort"
56
"time"
67

@@ -24,69 +25,42 @@ func (k Keeper) GetCurrentPrice(ctx sdk.Context, assetCode dnTypes.AssetCode) ty
2425
return price
2526
}
2627

27-
// GetRawPrices fetches the set of all prices posted by oracles for an asset and specific blockHeight.
28-
func (k Keeper) GetRawPrices(ctx sdk.Context, assetCode dnTypes.AssetCode, blockHeight int64) []types.PostedPrice {
28+
// GetCurrentPricesList returns all current prices.
29+
func (k Keeper) GetCurrentPricesList(ctx sdk.Context) (types.CurrentPrices, error) {
2930
k.modulePerms.AutoCheck(types.PermRead)
3031

3132
store := ctx.KVStore(k.storeKey)
32-
bz := store.Get(types.GetRawPricesKey(assetCode, blockHeight))
33+
iterator := sdk.KVStorePrefixIterator(store, types.GetCurrentPricePrefix())
34+
defer iterator.Close()
3335

34-
var prices []types.PostedPrice
35-
k.cdc.MustUnmarshalBinaryBare(bz, &prices)
36+
currentPrices := types.CurrentPrices{}
3637

37-
return prices
38-
}
38+
for ; iterator.Valid(); iterator.Next() {
39+
cPrice := types.CurrentPrice{}
40+
if err := k.cdc.UnmarshalBinaryBare(iterator.Value(), &cPrice); err != nil {
41+
err = fmt.Errorf("order unmarshal: %w", err)
42+
return nil, err
43+
}
44+
currentPrices = append(currentPrices, cPrice)
45+
}
3946

40-
// SetPrice updates the posted price for a specific oracle.
41-
func (k Keeper) SetPrice(
42-
ctx sdk.Context,
43-
oracle sdk.AccAddress,
44-
assetCode dnTypes.AssetCode,
45-
price sdk.Int,
46-
receivedAt time.Time) (types.PostedPrice, error) {
47+
return currentPrices, nil
48+
}
4749

50+
// addCurrentPrice adds currentPrice item to the storage.
51+
func (k Keeper) addCurrentPrice(ctx sdk.Context, currentPrice types.CurrentPrice) {
4852
k.modulePerms.AutoCheck(types.PermWrite)
4953

50-
// validate price receivedAt timestamp comparing to the current blockHeight timestamp
51-
if err := k.checkPriceReceivedAtTimestamp(ctx, receivedAt); err != nil {
52-
return types.PostedPrice{}, err
53-
}
54-
55-
// find raw price for specified oracle
5654
store := ctx.KVStore(k.storeKey)
57-
prices := k.GetRawPrices(ctx, assetCode, ctx.BlockHeight())
58-
var index int
59-
found := false
60-
for i := range prices {
61-
if prices[i].OracleAddress.Equals(oracle) {
62-
index = i
63-
found = true
64-
break
65-
}
66-
}
6755

68-
// set the rawPrice for that particular oracle
69-
if found {
70-
prices[index] = types.PostedPrice{
71-
AssetCode: assetCode, OracleAddress: oracle,
72-
Price: price, ReceivedAt: receivedAt}
73-
} else {
74-
prices = append(prices, types.PostedPrice{
75-
AssetCode: assetCode, OracleAddress: oracle,
76-
Price: price, ReceivedAt: receivedAt})
77-
index = len(prices) - 1
78-
}
79-
80-
store.Set(types.GetRawPricesKey(assetCode, ctx.BlockHeight()), k.cdc.MustMarshalBinaryBare(prices))
81-
82-
return prices[index], nil
56+
bz := k.cdc.MustMarshalBinaryBare(currentPrice)
57+
store.Set(types.GetCurrentPriceKey(currentPrice.AssetCode), bz)
8358
}
8459

8560
// SetCurrentPrices updates the price of an asset to the median of all valid oracle inputs and cleans up previous inputs.
8661
func (k Keeper) SetCurrentPrices(ctx sdk.Context) error {
8762
k.modulePerms.AutoCheck(types.PermWrite)
8863

89-
store := ctx.KVStore(k.storeKey)
9064
assets := k.GetAssetParams(ctx)
9165

9266
updatesCnt := 0
@@ -144,7 +118,7 @@ func (k Keeper) SetCurrentPrices(ctx sdk.Context) error {
144118
ReceivedAt: medianReceivedAt,
145119
}
146120

147-
store.Set(types.GetCurrentPriceKey(assetCode), k.cdc.MustMarshalBinaryBare(newPrice))
121+
k.addCurrentPrice(ctx, newPrice)
148122

149123
// save price to VM storage
150124
priceVmAccessPath, priceVmValue := types.NewResPriceStorageValuesPanic(newPrice.AssetCode, newPrice.Price)
@@ -162,6 +136,64 @@ func (k Keeper) SetCurrentPrices(ctx sdk.Context) error {
162136
return nil
163137
}
164138

139+
// GetRawPrices fetches the set of all prices posted by oracles for an asset and specific blockHeight.
140+
func (k Keeper) GetRawPrices(ctx sdk.Context, assetCode dnTypes.AssetCode, blockHeight int64) []types.PostedPrice {
141+
k.modulePerms.AutoCheck(types.PermRead)
142+
143+
store := ctx.KVStore(k.storeKey)
144+
bz := store.Get(types.GetRawPricesKey(assetCode, blockHeight))
145+
146+
var prices []types.PostedPrice
147+
k.cdc.MustUnmarshalBinaryBare(bz, &prices)
148+
149+
return prices
150+
}
151+
152+
// SetPrice updates the posted price for a specific oracle.
153+
func (k Keeper) SetPrice(
154+
ctx sdk.Context,
155+
oracle sdk.AccAddress,
156+
assetCode dnTypes.AssetCode,
157+
price sdk.Int,
158+
receivedAt time.Time) (types.PostedPrice, error) {
159+
160+
k.modulePerms.AutoCheck(types.PermWrite)
161+
162+
// validate price receivedAt timestamp comparing to the current blockHeight timestamp
163+
if err := k.checkPriceReceivedAtTimestamp(ctx, receivedAt); err != nil {
164+
return types.PostedPrice{}, err
165+
}
166+
167+
// find raw price for specified oracle
168+
store := ctx.KVStore(k.storeKey)
169+
prices := k.GetRawPrices(ctx, assetCode, ctx.BlockHeight())
170+
var index int
171+
found := false
172+
for i := range prices {
173+
if prices[i].OracleAddress.Equals(oracle) {
174+
index = i
175+
found = true
176+
break
177+
}
178+
}
179+
180+
// set the rawPrice for that particular oracle
181+
if found {
182+
prices[index] = types.PostedPrice{
183+
AssetCode: assetCode, OracleAddress: oracle,
184+
Price: price, ReceivedAt: receivedAt}
185+
} else {
186+
prices = append(prices, types.PostedPrice{
187+
AssetCode: assetCode, OracleAddress: oracle,
188+
Price: price, ReceivedAt: receivedAt})
189+
index = len(prices) - 1
190+
}
191+
192+
store.Set(types.GetRawPricesKey(assetCode, ctx.BlockHeight()), k.cdc.MustMarshalBinaryBare(prices))
193+
194+
return prices[index], nil
195+
}
196+
165197
// nolint:errcheck
166198
// ValidatePostPrice makes sure the person posting the price is an oracle.
167199
func (k Keeper) ValidatePostPrice(ctx sdk.Context, msg types.MsgPostPrice) error {

0 commit comments

Comments
 (0)