From 31d3bdbc082dee2c033fcc3b8e1c92ace63c3add Mon Sep 17 00:00:00 2001 From: Unique-Divine Date: Thu, 28 Dec 2023 00:59:25 -0600 Subject: [PATCH] impl wip! keeper version without msg server or protos --- app/keepers.go | 12 ++-- x/oracle/keeper/keeper.go | 22 ++++-- x/oracle/keeper/sudo.go | 109 ++++++++++++++++++++++++++++++ x/oracle/keeper/sudo_test.go | 65 ++++++++++++++++++ x/oracle/keeper/test_utils.go | 23 ++++++- x/oracle/types/expected_keeper.go | 7 ++ x/perp/v2/keeper/admin.go | 9 ++- 7 files changed, 231 insertions(+), 16 deletions(-) create mode 100644 x/oracle/keeper/sudo.go create mode 100644 x/oracle/keeper/sudo_test.go diff --git a/app/keepers.go b/app/keepers.go index cb8e193e0..874321829 100644 --- a/app/keepers.go +++ b/app/keepers.go @@ -357,18 +357,20 @@ func (app *NibiruApp) InitKeepers( appCodec, keys[spottypes.StoreKey], app.GetSubspace(spottypes.ModuleName), app.AccountKeeper, app.BankKeeper, app.DistrKeeper) + app.SudoKeeper = keeper.NewKeeper( + appCodec, keys[sudotypes.StoreKey], + ) + app.OracleKeeper = oraclekeeper.NewKeeper(appCodec, keys[oracletypes.StoreKey], - app.AccountKeeper, app.BankKeeper, app.DistrKeeper, app.stakingKeeper, distrtypes.ModuleName, + app.AccountKeeper, app.BankKeeper, app.DistrKeeper, app.stakingKeeper, + app.SudoKeeper, + distrtypes.ModuleName, ) app.EpochsKeeper = epochskeeper.NewKeeper( appCodec, keys[epochstypes.StoreKey], ) - app.SudoKeeper = keeper.NewKeeper( - appCodec, keys[sudotypes.StoreKey], - ) - app.PerpKeeperV2 = perpkeeper.NewKeeper( appCodec, keys[perptypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.OracleKeeper, app.EpochsKeeper, diff --git a/x/oracle/keeper/keeper.go b/x/oracle/keeper/keeper.go index c8031720e..5eb5127f1 100644 --- a/x/oracle/keeper/keeper.go +++ b/x/oracle/keeper/keeper.go @@ -27,9 +27,13 @@ type Keeper struct { bankKeeper types.BankKeeper distrKeeper types.DistributionKeeper StakingKeeper types.StakingKeeper + SudoKeeper types.SudoKeeper distrModuleName string + // Extends the Keeper with sudo functions. See sudo.go. + Admin admin + Params collections.Item[types.Params] ExchangeRates collections.Map[asset.Pair, types.DatedPrice] FeederDelegations collections.Map[sdk.ValAddress, sdk.AccAddress] @@ -45,23 +49,31 @@ type Keeper struct { } // NewKeeper constructs a new keeper for oracle -func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, +func NewKeeper( + cdc codec.BinaryCodec, + storeKey storetypes.StoreKey, + accountKeeper types.AccountKeeper, - bankKeeper types.BankKeeper, distrKeeper types.DistributionKeeper, - stakingKeeper types.StakingKeeper, distrName string, + bankKeeper types.BankKeeper, + distrKeeper types.DistributionKeeper, + stakingKeeper types.StakingKeeper, + sudoKeeper types.SudoKeeper, + + distrName string, ) Keeper { // ensure oracle module account is set if addr := accountKeeper.GetModuleAddress(types.ModuleName); addr == nil { panic(fmt.Sprintf("%s module account has not been set", types.ModuleName)) } - return Keeper{ + k := Keeper{ cdc: cdc, storeKey: storeKey, AccountKeeper: accountKeeper, bankKeeper: bankKeeper, distrKeeper: distrKeeper, StakingKeeper: stakingKeeper, + SudoKeeper: sudoKeeper, distrModuleName: distrName, Params: collections.NewItem(storeKey, 11, collections.ProtoValueEncoder[types.Params](cdc)), ExchangeRates: collections.NewMap(storeKey, 1, asset.PairKeyEncoder, collections.ProtoValueEncoder[types.DatedPrice](cdc)), @@ -76,6 +88,8 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, collections.Uint64KeyEncoder, collections.ProtoValueEncoder[types.Rewards](cdc)), RewardsID: collections.NewSequence(storeKey, 9), } + k.Admin = admin{&k} + return k } // Logger returns a module-specific logger. diff --git a/x/oracle/keeper/sudo.go b/x/oracle/keeper/sudo.go new file mode 100644 index 000000000..b71153d39 --- /dev/null +++ b/x/oracle/keeper/sudo.go @@ -0,0 +1,109 @@ +package keeper + +import ( + "fmt" + "time" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/x/common/asset" + oracletypes "github.com/NibiruChain/nibiru/x/oracle/types" +) + +// Extends the Keeper with admin functions. Admin is syntactic sugar to separate +// admin calls off from the other Keeper methods. +// +// These Admin functions should: +// 1. Not be called in other methods in the x/perp module. +// 2. Only be callable from the x/sudo root or sudo contracts. +// +// The intention behind "admin" is to make it more obvious to the developer that +// an unsafe function is being used when it's called from "OracleKeeper.Admin" +type admin struct{ *Keeper } + +type PartialOracleParams struct { + VotePeriod *sdkmath.Int `json:"vote_period,omitempty"` + VoteThreshold *sdk.Dec `json:"vote_threshold,omitempty"` + RewardBand *sdk.Dec `json:"reward_band,omitempty"` + Whitelist []string `json:"whitelist,omitempty"` + SlashFraction *sdk.Dec `json:"slash_fraction,omitempty"` + SlashWindow *sdkmath.Int `json:"slash_window,omitempty"` + MinValidPerWindow *sdk.Dec `json:"min_valid_per_window,omitempty"` + TwapLookbackWindow *sdkmath.Int `json:"twap_lookback_window,omitempty"` + MinVoters *sdkmath.Int `json:"min_voters,omitempty"` + ValidatorFeeRatio *sdk.Dec `json:"validator_fee_ratio,omitempty"` +} + +func (k admin) EditOracleParams( + ctx sdk.Context, newParams PartialOracleParams, sender sdk.AccAddress, +) error { + if err := k.SudoKeeper.CheckPermissions(sender, ctx); err != nil { + return err + } + + params, err := k.Params.Get(ctx) + if err != nil { + // TODO: use typed error + return fmt.Errorf("get oracle params error: %s", err.Error()) + } + + mergedParams := newParams.mergeOracleParams(params) + k.UpdateParams(ctx, mergedParams) + return nil +} + +// mergeOracleParams: Takes the givne oracle params and merges them into the +// existing partial params, keeping any existing values that are note set in the +// partial msg +func (msg PartialOracleParams) mergeOracleParams( + oracleParams oracletypes.Params, +) oracletypes.Params { + if msg.VotePeriod != nil { + oracleParams.VotePeriod = msg.VotePeriod.Uint64() + } + + if msg.VoteThreshold != nil { + oracleParams.VoteThreshold = *msg.VoteThreshold + } + + if msg.RewardBand != nil { + oracleParams.RewardBand = *msg.RewardBand + } + + if msg.Whitelist != nil { + whitelist := make([]asset.Pair, len(msg.Whitelist)) + for i, pair := range msg.Whitelist { + whitelist[i] = asset.MustNewPair(pair) + } + + oracleParams.Whitelist = whitelist + } + + if msg.SlashFraction != nil { + oracleParams.SlashFraction = *msg.SlashFraction + } + + if msg.SlashWindow != nil { + oracleParams.SlashWindow = msg.SlashWindow.Uint64() + } + + if msg.MinValidPerWindow != nil { + oracleParams.MinValidPerWindow = *msg.MinValidPerWindow + } + + if msg.TwapLookbackWindow != nil { + oracleParams.TwapLookbackWindow = time.Duration(msg.TwapLookbackWindow.Int64()) + } + + if msg.MinVoters != nil { + oracleParams.MinVoters = msg.MinVoters.Uint64() + } + + if msg.ValidatorFeeRatio != nil { + oracleParams.ValidatorFeeRatio = *msg.ValidatorFeeRatio + } + + return oracleParams +} diff --git a/x/oracle/keeper/sudo_test.go b/x/oracle/keeper/sudo_test.go new file mode 100644 index 000000000..44751d199 --- /dev/null +++ b/x/oracle/keeper/sudo_test.go @@ -0,0 +1,65 @@ +package keeper_test + +import ( + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/x/common/testutil" + "github.com/NibiruChain/nibiru/x/common/testutil/testapp" + oraclekeeper "github.com/NibiruChain/nibiru/x/oracle/keeper" +) + +func TestSuiteOracleExecutor_RunAll(t *testing.T) { + suite.Run(t, new(SuiteOracleSudo)) +} + +type SuiteOracleSudo struct { + suite.Suite +} + +func (s *SuiteOracleSudo) TestEditOracleParams() { + nibiru, ctx := testapp.NewNibiruTestAppAndContext() + + // Change to all non-defaults to test EditOracleParams as a setter . + votePeriod := sdk.NewInt(1_234) + voteThreshold := sdk.MustNewDecFromStr("0.4") + rewardBand := sdk.MustNewDecFromStr("0.5") + whitelist := []string{"aave:usdc", "sol:usdc"} + slashFraction := sdk.MustNewDecFromStr("0.5") + slashWindow := sdk.NewInt(2) + minValidPerWindow := sdk.MustNewDecFromStr("0.5") + twapLookbackWindow := sdk.NewInt(int64(time.Second * 30)) + minVoters := sdk.NewInt(2) + validatorFeeRatio := sdk.MustNewDecFromStr("0.7") + partialParams := oraclekeeper.PartialOracleParams{ + VotePeriod: &votePeriod, + VoteThreshold: &voteThreshold, + RewardBand: &rewardBand, + Whitelist: whitelist, + SlashFraction: &slashFraction, + SlashWindow: &slashWindow, + MinValidPerWindow: &minValidPerWindow, + TwapLookbackWindow: &twapLookbackWindow, + MinVoters: &minVoters, + ValidatorFeeRatio: &validatorFeeRatio, + } + + // TODO: Verify that params before were not equal + + invalidSender := testutil.AccAddress() + err := nibiru.OracleKeeper.Admin.EditOracleParams( + ctx, partialParams, invalidSender, + ) + s.Error(err) + + okSender := testapp.DefaultSudoRoot() + err = nibiru.OracleKeeper.Admin.EditOracleParams( + ctx, partialParams, okSender, + ) + s.NoError(err) + + // call admin method without err +} diff --git a/x/oracle/keeper/test_utils.go b/x/oracle/keeper/test_utils.go index 5cef19d0e..d5b94e807 100644 --- a/x/oracle/keeper/test_utils.go +++ b/x/oracle/keeper/test_utils.go @@ -13,6 +13,9 @@ import ( "github.com/NibiruChain/nibiru/x/common/denoms" "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/x/sudo" + sudokeeper "github.com/NibiruChain/nibiru/x/sudo/keeper" + sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" dbm "github.com/cometbft/cometbft-db" "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/secp256k1" @@ -51,6 +54,7 @@ var ModuleBasics = module.NewBasicManager( distr.AppModuleBasic{}, staking.AppModuleBasic{}, params.AppModuleBasic{}, + sudo.AppModuleBasic{}, ) // MakeTestCodec nolint @@ -123,6 +127,7 @@ type TestFixture struct { OracleKeeper Keeper StakingKeeper stakingkeeper.Keeper DistrKeeper distrkeeper.Keeper + SudoKeeper types.SudoKeeper } // CreateTestFixture nolint @@ -135,6 +140,7 @@ func CreateTestFixture(t *testing.T) TestFixture { keyOracle := sdk.NewKVStoreKey(types.StoreKey) keyStaking := sdk.NewKVStoreKey(stakingtypes.StoreKey) keyDistr := sdk.NewKVStoreKey(distrtypes.StoreKey) + keySudo := sdk.NewKVStoreKey(sudotypes.StoreKey) db := dbm.NewMemDB() ms := store.NewCommitMultiStore(db) @@ -224,11 +230,15 @@ func CreateTestFixture(t *testing.T) TestFixture { bankKeeper.SendCoinsFromModuleToModule(ctx, faucetAccountName, stakingtypes.NotBondedPoolName, sdk.NewCoins(sdk.NewCoin(denoms.NIBI, InitTokens.MulRaw(int64(len(Addrs)))))) + sudoKeeper := sudokeeper.NewKeeper(appCodec, keySudo) + sudoAcc := authtypes.NewEmptyModuleAccount(sudotypes.ModuleName) + accountKeeper.SetModuleAccount(ctx, feeCollectorAcc) accountKeeper.SetModuleAccount(ctx, bondPool) accountKeeper.SetModuleAccount(ctx, notBondedPool) accountKeeper.SetModuleAccount(ctx, distrAcc) accountKeeper.SetModuleAccount(ctx, oracleAcc) + accountKeeper.SetModuleAccount(ctx, sudoAcc) for _, addr := range Addrs { accountKeeper.SetAccount(ctx, authtypes.NewBaseAccountWithAddress(addr)) @@ -243,6 +253,7 @@ func CreateTestFixture(t *testing.T) TestFixture { bankKeeper, distrKeeper, stakingKeeper, + sudoKeeper, distrtypes.ModuleName, ) @@ -254,11 +265,19 @@ func CreateTestFixture(t *testing.T) TestFixture { keeper.Params.Set(ctx, defaults) - return TestFixture{ctx, legacyAmino, accountKeeper, bankKeeper, keeper, *stakingKeeper, distrKeeper} + return TestFixture{ + ctx, legacyAmino, accountKeeper, bankKeeper, + keeper, + *stakingKeeper, + distrKeeper, + sudoKeeper, + } } // NewTestMsgCreateValidator test msg creator -func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey cryptotypes.PubKey, amt sdk.Int) *stakingtypes.MsgCreateValidator { +func NewTestMsgCreateValidator( + address sdk.ValAddress, pubKey cryptotypes.PubKey, amt sdk.Int, +) *stakingtypes.MsgCreateValidator { commission := stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) msg, _ := stakingtypes.NewMsgCreateValidator( address, pubKey, sdk.NewCoin(denoms.NIBI, amt), diff --git a/x/oracle/types/expected_keeper.go b/x/oracle/types/expected_keeper.go index 4c47f8bd5..0d53c0395 100644 --- a/x/oracle/types/expected_keeper.go +++ b/x/oracle/types/expected_keeper.go @@ -41,3 +41,10 @@ type BankKeeper interface { // only used for simulation SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins } + +type SudoKeeper interface { + // CheckPermissions Checks if a contract is contained within the set of sudo + // contracts defined in the x/sudo module. These smart contracts are able to + // execute certain permissioned functions. + CheckPermissions(contract sdk.AccAddress, ctx sdk.Context) error +} diff --git a/x/perp/v2/keeper/admin.go b/x/perp/v2/keeper/admin.go index 713a8900a..af1b49596 100644 --- a/x/perp/v2/keeper/admin.go +++ b/x/perp/v2/keeper/admin.go @@ -15,12 +15,11 @@ import ( // admin calls off from the other Keeper methods. // // These Admin functions should: -// 1. Not be wired into the MsgServer. -// 2. Not be called in other methods in the x/perp module. -// 3. Only be callable from nibiru/wasmbinding via sudo contracts. +// 1. Not be called in other methods in the x/perp module. +// 2. Only be callable from the x/sudo root or sudo contracts. // -// The intention here is to make it more obvious to the developer that an unsafe -// function is being used when it's called from the PerpKeeper.Admin struct. +// The intention behind "admin" is to make it more obvious to the developer that +// an unsafe function is being used when it's called from "PerpKeeper.Admin" type admin struct{ *Keeper } // WithdrawFromPerpFund sends funds from the Perp Fund to the "to" address.