Skip to content

Commit

Permalink
feat(oracle): wire to the App (#832)
Browse files Browse the repository at this point in the history
* add: wire oracle

* temp: attempt to fix localnet.sh

* upgrade changelog

* add: slash logging

* add: miss counters logging

* tmp

* fix: almost there....

* add: finalize first oracle scenario integration test

* add: function to find private key info for an address

* fix: test

* chore: move oracle to simapp

* chore: lint

* chore: docs and function simplification

* chore: CHANGELOG.md

* temp: move app_test.go to a separate pkg

Co-authored-by: Agent Smith <[email protected]>
  • Loading branch information
testinginprod and AgentSmithMatrix authored Aug 23, 2022
1 parent 6772d76 commit b3f0d0a
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 2 deletions.
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

0 comments on commit b3f0d0a

Please sign in to comment.