Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(oracle): wire to the App #832

Merged
merged 19 commits into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [#813](https://github.com/NibiruChain/nibiru/pull/813) - (vpool): Expose mark price, mark TWAP, index price, and k (swap invariant) in the all-pools query
* [#816](https://github.com/NibiruChain/nibiru/pull/816) - Remove tobin tax from x/oracle
* [#810](https://github.com/NibiruChain/nibiru/pull/810) - feat(x/perp): expose 'marginRatioIndex' and block number on QueryTraderPosition
* [#832](https://github.com/NibiruChain/nibiru/pull/832) - x/oracle app wiring

### Documentation

Expand Down
25 changes: 25 additions & 0 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
"os"
"path/filepath"

"github.com/NibiruChain/nibiru/x/oracle"
oraclekeeper "github.com/NibiruChain/nibiru/x/oracle/keeper"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
_ "github.com/cosmos/cosmos-sdk/client/docs/statik"
Expand Down Expand Up @@ -98,6 +101,8 @@ import (
tmos "github.com/tendermint/tendermint/libs/os"
dbm "github.com/tendermint/tm-db"

oracletypes "github.com/NibiruChain/nibiru/x/oracle/types"

nibiapp "github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/x/common"
"github.com/NibiruChain/nibiru/x/dex"
Expand Down Expand Up @@ -174,6 +179,7 @@ var (
ibctransfer.AppModuleBasic{},

// native x/
oracle.AppModuleBasic{},
dex.AppModuleBasic{},
pricefeed.AppModuleBasic{},
epochs.AppModuleBasic{},
Expand Down Expand Up @@ -201,6 +207,7 @@ var (
perptypes.FeePoolModuleAccount: {},
epochstypes.ModuleName: {},
lockuptypes.ModuleName: {authtypes.Minter, authtypes.Burner},
oracletypes.ModuleName: nil,
stablecointypes.StableEFModuleAccount: {authtypes.Burner},
common.TreasuryPoolModuleAccount: {},
}
Expand Down Expand Up @@ -269,6 +276,7 @@ type NibiruTestApp struct {
// ---------------
// Nibiru keepers
// ---------------
OracleKeeper oraclekeeper.Keeper
DexKeeper dexkeeper.Keeper
StablecoinKeeper stablecoinkeeper.Keeper
PerpKeeper perpkeeper.Keeper
Expand Down Expand Up @@ -332,6 +340,7 @@ func NewNibiruTestApp(
ibchost.StoreKey,
ibctransfertypes.StoreKey,
// nibiru x/ keys
oracletypes.StoreKey,
dextypes.StoreKey,
pricefeedtypes.StoreKey,
stablecointypes.StoreKey,
Expand Down Expand Up @@ -423,6 +432,14 @@ func NewNibiruTestApp(

// ---------------------------------- Nibiru Chain x/ keepers

app.OracleKeeper = oraclekeeper.NewKeeper(
appCodec,
keys[oracletypes.StoreKey],
app.GetSubspace(oracletypes.ModuleName),
app.AccountKeeper, app.BankKeeper, app.DistrKeeper, &stakingKeeper,
distrtypes.ModuleName,
)

app.DexKeeper = dexkeeper.NewKeeper(
appCodec, keys[dextypes.StoreKey], app.GetSubspace(dextypes.ModuleName),
app.AccountKeeper, app.BankKeeper, app.DistrKeeper)
Expand Down Expand Up @@ -534,6 +551,9 @@ func NewNibiruTestApp(
var skipGenesisInvariants = cast.ToBool(
appOpts.Get(crisis.FlagSkipGenesisInvariants))

oracleModule := oracle.NewAppModule(
appCodec, app.OracleKeeper, app.AccountKeeper, app.BankKeeper)

dexModule := dex.NewAppModule(
appCodec, app.DexKeeper, app.AccountKeeper, app.BankKeeper)
pricefeedModule := pricefeed.NewAppModule(
Expand Down Expand Up @@ -575,6 +595,7 @@ func NewNibiruTestApp(
params.NewAppModule(app.ParamsKeeper),
authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
// native x/
oracleModule,
dexModule,
pricefeedModule,
stablecoinModule,
Expand Down Expand Up @@ -620,13 +641,15 @@ func NewNibiruTestApp(
perptypes.ModuleName,
lockuptypes.ModuleName,
incentivizationtypes.ModuleName,
oracletypes.ModuleName,
// ibc modules
ibchost.ModuleName,
ibctransfertypes.ModuleName,
)
app.mm.SetOrderEndBlockers(
crisistypes.ModuleName,
govtypes.ModuleName,
oracletypes.ModuleName,
stakingtypes.ModuleName,
capabilitytypes.ModuleName,
authtypes.ModuleName,
Expand Down Expand Up @@ -678,6 +701,7 @@ func NewNibiruTestApp(
upgradetypes.ModuleName,
vestingtypes.ModuleName,
// native x/
oracletypes.ModuleName,
dextypes.ModuleName,
pricefeedtypes.ModuleName,
epochstypes.ModuleName,
Expand Down Expand Up @@ -992,6 +1016,7 @@ func initParamsKeeper(
paramsKeeper.Subspace(pricefeedtypes.ModuleName)
paramsKeeper.Subspace(epochstypes.ModuleName)
paramsKeeper.Subspace(stablecointypes.ModuleName)
paramsKeeper.Subspace(oracletypes.ModuleName)
// ibc params keepers
paramsKeeper.Subspace(ibctransfertypes.ModuleName)
paramsKeeper.Subspace(ibchost.ModuleName)
Expand Down
2 changes: 2 additions & 0 deletions x/oracle/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) {

params := k.GetParams(ctx)
if types.IsPeriodLastBlock(ctx, params.VotePeriod) {
k.Logger(ctx).Info("processing validator price votes")
// Build claim map over all validators in active set
validatorClaimMap := make(map[string]types.Claim)

Expand Down Expand Up @@ -91,6 +92,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) {

// Increase miss counter
k.SetMissCounter(ctx, claim.Recipient, k.GetMissCounter(ctx, claim.Recipient)+1)
k.Logger(ctx).Info("vote miss", "validator", claim.Recipient.String())
}

// Distribute rewards to ballot winners
Expand Down
160 changes: 160 additions & 0 deletions x/oracle/integration_test/app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package integration_test_test

import (
"context"
"testing"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"

"github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/simapp"
oracletypes "github.com/NibiruChain/nibiru/x/oracle/types"
testutilcli "github.com/NibiruChain/nibiru/x/testutil/cli"
)

type IntegrationTestSuite struct {
suite.Suite

cfg testutilcli.Config
network *testutilcli.Network
}

func (s *IntegrationTestSuite) SetupTest() {
app.SetPrefixes(app.AccountAddressPrefix)
s.cfg = testutilcli.BuildNetworkConfig(simapp.NewTestGenesisStateFromDefault())
s.cfg.NumValidators = 4
s.cfg.GenesisState[oracletypes.ModuleName] = s.cfg.Codec.MustMarshalJSON(func() codec.ProtoMarshaler {
gs := oracletypes.DefaultGenesisState()
gs.Params.Whitelist = oracletypes.PairList{
oracletypes.Pair{Name: "nibi:usdc"},
oracletypes.Pair{Name: "btc:usdc"},
}

return gs
}())

s.network = testutilcli.NewNetwork(s.T(), s.cfg)
_, err := s.network.WaitForHeight(2)
require.NoError(s.T(), err)
}

func (s *IntegrationTestSuite) TestSuccessfulVoting() {
// assuming validators have equal power
// we use the weighted median.
// what happens is that prices are ordered
// based on exchange rate, from lowest to highest.
// then the median is picked, based on consensus power
// so obviously, in this case, since validators have the same power
// once weight (based on power) >= total power (sum of weights)
// then the number picked is the one in the middle always.
prices := []map[string]sdk.Dec{
{
"nibi:usdc": sdk.MustNewDecFromStr("1"),
"btc:usdc": sdk.MustNewDecFromStr("100203.0"),
},
{
"nibi:usdc": sdk.MustNewDecFromStr("1"),
"btc:usdc": sdk.MustNewDecFromStr("100150.5"),
},
{
"nibi:usdc": sdk.MustNewDecFromStr("1"),
"btc:usdc": sdk.MustNewDecFromStr("100200.9"),
},
{
"nibi:usdc": sdk.MustNewDecFromStr("1"),
"btc:usdc": sdk.MustNewDecFromStr("100300.9"),
},
}
votes := s.sendPrevotes(prices)

s.waitVoteRevealBlock()

s.sendVotes(votes)

s.waitPriceUpdateBlock()

gotPrices := s.currentPrices()
require.Equal(s.T(),
map[string]sdk.Dec{
"nibi:usdc": sdk.MustNewDecFromStr("1"),
"btc:usdc": sdk.MustNewDecFromStr("100200.9"),
},
gotPrices,
)
}

func (s *IntegrationTestSuite) sendPrevotes(prices []map[string]sdk.Dec) []string {
strVotes := make([]string, len(prices))
for i, val := range s.network.Validators {
raw := prices[i]
votes := make(oracletypes.ExchangeRateTuples, 0, len(raw))
for pair, price := range raw {
votes = append(votes, oracletypes.NewExchangeRateTuple(pair, price))
}

pricesStr, err := votes.ToString()
require.NoError(s.T(), err)
_, err = s.network.SendTx(val.Address, &oracletypes.MsgAggregateExchangeRatePrevote{
Hash: oracletypes.GetAggregateVoteHash("1", pricesStr, val.ValAddress).String(),
Feeder: val.Address.String(),
Validator: val.ValAddress.String(),
})
require.NoError(s.T(), err)

strVotes[i] = pricesStr
}

return strVotes
}

func (s *IntegrationTestSuite) sendVotes(rates []string) {
for i, val := range s.network.Validators {
_, err := s.network.SendTx(val.Address, &oracletypes.MsgAggregateExchangeRateVote{
Salt: "1",
ExchangeRates: rates[i],
Feeder: val.Address.String(),
Validator: val.ValAddress.String(),
})
require.NoError(s.T(), err)
}
}

func (s *IntegrationTestSuite) waitVoteRevealBlock() {
params, err := oracletypes.NewQueryClient(s.network.Validators[0].ClientCtx).Params(context.Background(), &oracletypes.QueryParamsRequest{})
require.NoError(s.T(), err)

votePeriod := params.Params.VotePeriod

height, err := s.network.LatestHeight()
require.NoError(s.T(), err)

waitBlock := (uint64(height)/votePeriod)*votePeriod + votePeriod

_, err = s.network.WaitForHeight(int64(waitBlock + 1))
require.NoError(s.T(), err)
}

// it's an alias, but it exists to give better understanding of what we're doing in test cases scenarios
func (s *IntegrationTestSuite) waitPriceUpdateBlock() {
s.waitVoteRevealBlock()
}

func (s *IntegrationTestSuite) currentPrices() map[string]sdk.Dec {
rawRates, err := oracletypes.NewQueryClient(s.network.Validators[0].ClientCtx).ExchangeRates(context.Background(), &oracletypes.QueryExchangeRatesRequest{})
require.NoError(s.T(), err)

prices := make(map[string]sdk.Dec)

for _, p := range rawRates.ExchangeRates {
prices[p.Pair] = p.ExchangeRate
}

return prices
}

func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(IntegrationTestSuite))
}
5 changes: 4 additions & 1 deletion x/oracle/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ func (ms msgServer) AggregateExchangeRateVote(goCtx context.Context, msg *types.

// Check a msg is submitted proper period
if (uint64(ctx.BlockHeight())/params.VotePeriod)-(aggregatePrevote.SubmitBlock/params.VotePeriod) != 1 {
return nil, types.ErrRevealPeriodMissMatch
return nil, types.ErrRevealPeriodMissMatch.Wrapf(
"aggregate prevote block: %d, current block: %d, vote period: %d",
aggregatePrevote.SubmitBlock, ctx.BlockHeight(), params.VotePeriod,
)
}

exchangeRateTuples, err := types.ParseExchangeRateTuples(msg.ExchangeRates)
Expand Down
1 change: 1 addition & 0 deletions x/oracle/keeper/slash.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func (k Keeper) SlashAndResetMissCounters(ctx sdk.Context) {
ctx, consAddr,
distributionHeight, validator.GetConsensusPower(powerReduction), slashFraction,
)
k.Logger(ctx).Info("slash", "validator", consAddr.String(), "fraction", slashFraction.String())
k.StakingKeeper.Jail(ctx, consAddr)
}
}
Expand Down
2 changes: 1 addition & 1 deletion x/oracle/keeper/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper

import (
"github.com/NibiruChain/nibiru/x/common"
"github.com/cosmos/cosmos-sdk/std"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
Expand All @@ -26,7 +27,6 @@ import (
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/simapp"
simparams "github.com/cosmos/cosmos-sdk/simapp/params"
"github.com/cosmos/cosmos-sdk/std"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
Expand Down
12 changes: 12 additions & 0 deletions x/testutil/cli/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,3 +517,15 @@ func (n *Network) Cleanup() {

n.T.Log("finished cleaning up test network")
}

func (n *Network) keyBaseAndInfoForAddr(addr sdk.AccAddress) (keyring.Keyring, keyring.Info) {
for _, v := range n.Validators {
info, err := v.ClientCtx.Keyring.KeyByAddress(addr)
if err == nil {
return v.ClientCtx.Keyring, info
}
}

n.T.Fatalf("address not found in any of the known validators keyrings: %s", addr.String())
return nil, nil
}
Loading