Skip to content

Commit

Permalink
feat: integrate emergency group with IBC Quota (#2191)
Browse files Browse the repository at this point in the history
* feat: integrate emergency group with IBC Quota

* fix import cycle

* emergency UpdateQuotaParams

* emergency GovSetIBCStatus

* tests

* lint
  • Loading branch information
robert-zaremba authored Aug 10, 2023
1 parent 43d0b74 commit 5efdbd4
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 23 deletions.
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ func New(
// UIbcQuotaKeeper implements ibcporttypes.ICS4Wrapper
app.UIbcQuotaKeeperB = uibcquotakeeper.NewKeeperBuilder(
appCodec, keys[uibc.StoreKey],
app.LeverageKeeper, uibcoracle.FromUmeeAvgPriceOracle(app.OracleKeeper),
app.LeverageKeeper, uibcoracle.FromUmeeAvgPriceOracle(app.OracleKeeper), app.UGovKeeperB.EmergencyGroup,
)

/**********
Expand Down
23 changes: 23 additions & 0 deletions util/checkers/proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,29 @@ func IsGovAuthority(authority string) bool {
return authority == govModuleAddr
}

// WithEmergencyGroup is a copy of ugov.WithEmergencyGroup to avoid import cycle
type WithEmergencyGroup interface {
EmergencyGroup() sdk.AccAddress
}

// EmergencyGroupAuthority returns true if the authority is EmergencyGroup. Returns false if
// authority is the x/gov address. Returns error otherwise.
// Note: we use WithEmergencyGroup rather than emergency group AccAddress to avoid storage read
// if it's not necessary.
func EmergencyGroupAuthority(authority string, eg WithEmergencyGroup) (bool, error) {
if IsGovAuthority(authority) {
return false, nil
}
a, err := sdk.AccAddressFromBech32(authority)
if err != nil {
return false, sdkerrors.ErrInvalidAddress.Wrapf("Authority: %v", err)
}
if !eg.EmergencyGroup().Equals(a) {
return false, sdkerrors.ErrUnauthorized
}
return true, nil
}

// ValidateProposal checks the format of the title, description.
// If `requireGov=true` then authority must be a gov module address. Otherwise authority must be
// a correct bech32 address.
Expand Down
15 changes: 4 additions & 11 deletions x/leverage/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"

sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/umee-network/umee/v6/util/checkers"
"github.com/umee-network/umee/v6/util/coin"
"github.com/umee-network/umee/v6/util/sdkutil"
Expand Down Expand Up @@ -547,18 +546,12 @@ func (s msgServer) GovUpdateRegistry(
regDenoms[token.BaseDenom] = token
}

byEmergencyGroup := !checkers.IsGovAuthority(msg.Authority)
if byEmergencyGroup {
a, err := sdk.AccAddressFromBech32(msg.Authority)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("Authority: %v", err)
}
if !s.keeper.ugov(&ctx).EmergencyGroup().Equals(a) {
return nil, sdkerrors.ErrUnauthorized
}
byEmergencyGroup, err := checkers.EmergencyGroupAuthority(msg.Authority, s.keeper.ugov(&ctx))
if err != nil {
return nil, err
}

err := s.keeper.UpdateTokenRegistry(ctx, msg.UpdateTokens, msg.AddTokens, regDenoms, byEmergencyGroup)
err = s.keeper.UpdateTokenRegistry(ctx, msg.UpdateTokens, msg.AddTokens, regDenoms, byEmergencyGroup)
if err != nil {
return nil, err
}
Expand Down
4 changes: 3 additions & 1 deletion x/uibc/quota/ics4_wrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
lfixtures "github.com/umee-network/umee/v6/x/leverage/fixtures"
ltypes "github.com/umee-network/umee/v6/x/leverage/types"
"github.com/umee-network/umee/v6/x/oracle/types"
ugovmocks "github.com/umee-network/umee/v6/x/ugov/mocks"
"github.com/umee-network/umee/v6/x/uibc"
"github.com/umee-network/umee/v6/x/uibc/mocks"
"github.com/umee-network/umee/v6/x/uibc/quota"
Expand Down Expand Up @@ -47,11 +48,12 @@ func TestSendPacket(t *testing.T) {

leverageMock := mocks.NewMockLeverage(ctrl)
oracleMock := mocks.NewMockOracle(ctrl)
eg := ugovmocks.NewSimpleEmergencyGroupBuilder()
mock := MockICS4Wrapper{}

storeKey := storetypes.NewMemoryStoreKey("quota")
ctx, _ := tsdk.NewCtxOneStore(t, storeKey)
kb := keeper.NewKeeperBuilder(codec.NewProtoCodec(nil), storeKey, leverageMock, oracleMock)
kb := keeper.NewKeeperBuilder(codec.NewProtoCodec(nil), storeKey, leverageMock, oracleMock, eg)
dp := uibc.DefaultParams()
keeper := kb.Keeper(&ctx)
keeper.SetParams(dp)
Expand Down
4 changes: 3 additions & 1 deletion x/uibc/quota/keeper/intest/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
umeeapp "github.com/umee-network/umee/v6/app"
appparams "github.com/umee-network/umee/v6/app/params"
"github.com/umee-network/umee/v6/tests/tsdk"
ugovmocks "github.com/umee-network/umee/v6/x/ugov/mocks"
"github.com/umee-network/umee/v6/x/uibc"
"github.com/umee-network/umee/v6/x/uibc/quota/keeper"
)
Expand Down Expand Up @@ -104,6 +105,7 @@ func initKeeper(
) (sdk.Context, keeper.Keeper) {
storeKey := storetypes.NewMemoryStoreKey("quota")
ctx, _ := tsdk.NewCtxOneStore(t, storeKey)
kb := keeper.NewKeeperBuilder(cdc, storeKey, leverage, oracle)
eg := ugovmocks.NewSimpleEmergencyGroupBuilder()
kb := keeper.NewKeeperBuilder(cdc, storeKey, leverage, oracle, eg)
return ctx, kb.Keeper(&ctx)
}
8 changes: 7 additions & 1 deletion x/uibc/quota/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/umee-network/umee/v6/x/ugov"
"github.com/umee-network/umee/v6/x/uibc"
)

Expand All @@ -18,16 +19,19 @@ type Builder struct {
cdc codec.BinaryCodec
leverage uibc.Leverage
oracle uibc.Oracle
ugov ugov.EmergencyGroupBuilder
}

func NewKeeperBuilder(
cdc codec.BinaryCodec, key storetypes.StoreKey, leverage uibc.Leverage, oracle uibc.Oracle,
cdc codec.BinaryCodec, key storetypes.StoreKey,
leverage uibc.Leverage, oracle uibc.Oracle, ugov ugov.EmergencyGroupBuilder,
) Builder {
return Builder{
cdc: cdc,
storeKey: key,
leverage: leverage,
oracle: oracle,
ugov: ugov,
}
}

Expand All @@ -36,6 +40,7 @@ func (kb Builder) Keeper(ctx *sdk.Context) Keeper {
store: ctx.KVStore(kb.storeKey),
leverage: kb.leverage,
oracle: kb.oracle,
ugov: kb.ugov(ctx),
cdc: kb.cdc,
blockTime: ctx.BlockTime(),

Expand All @@ -50,6 +55,7 @@ type Keeper struct {
store sdk.KVStore
leverage uibc.Leverage
oracle uibc.Oracle
ugov ugov.WithEmergencyGroup

/**
if Keeper methods depends on sdk.Context, then we should add those dependencies directly,
Expand Down
13 changes: 12 additions & 1 deletion x/uibc/quota/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"context"

"github.com/umee-network/umee/v6/util/checkers"
"github.com/umee-network/umee/v6/util/sdkutil"
"github.com/umee-network/umee/v6/x/uibc"
)
Expand All @@ -28,7 +29,12 @@ func (m msgServer) GovUpdateQuota(ctx context.Context, msg *uibc.MsgGovUpdateQuo
}

k := m.kb.Keeper(&sdkCtx)
if err := k.UpdateQuotaParams(msg.Total, msg.PerDenom, msg.QuotaDuration); err != nil {
byEmergencyGroup, err := checkers.EmergencyGroupAuthority(msg.Authority, k.ugov)
if err != nil {
return nil, err
}

if err := k.UpdateQuotaParams(msg.Total, msg.PerDenom, msg.QuotaDuration, byEmergencyGroup); err != nil {
return nil, err
}
return &uibc.MsgGovUpdateQuotaResponse{}, nil
Expand All @@ -44,6 +50,11 @@ func (m msgServer) GovSetIBCStatus(
}

k := m.kb.Keeper(&sdkCtx)
// emergency group can change status to any valid value
if _, err = checkers.EmergencyGroupAuthority(msg.Authority, k.ugov); err != nil {
return nil, err
}

if err := k.SetIBCStatus(msg.IbcStatus); err != nil {
return &uibc.MsgGovSetIBCStatusResponse{}, err
}
Expand Down
34 changes: 28 additions & 6 deletions x/uibc/quota/keeper/params.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper

import (
"errors"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -31,13 +32,34 @@ func (k Keeper) GetParams() (params uibc.Params) {

// UpdateQuotaParams update the ibc-transfer quota params for ibc denoms
func (k Keeper) UpdateQuotaParams(totalQuota, quotaPerDenom sdk.Dec, quotaDuration time.Duration,
) error {
params := k.GetParams()
params.TotalQuota = totalQuota
params.QuotaDuration = quotaDuration
params.TokenQuota = quotaPerDenom
byEmergencyGroup bool) error {

return k.SetParams(params)
pOld := k.GetParams()
pNew := pOld
pNew.TotalQuota = totalQuota
pNew.QuotaDuration = quotaDuration
pNew.TokenQuota = quotaPerDenom
if byEmergencyGroup {
if err := validateEmergencyQuotaParamsUpdate(pOld, pNew); err != nil {
return err
}
}

return k.SetParams(pNew)
}

func validateEmergencyQuotaParamsUpdate(pOld, pNew uibc.Params) error {
var errs []error
if pNew.TotalQuota.GT(pOld.TotalQuota) || pNew.TokenQuota.GT(pOld.TokenQuota) {
errs = append(errs, errors.New("emergency group can't increase TotalQuota nor TokenQuota"))
}
if pNew.QuotaDuration != pOld.QuotaDuration {
errs = append(errs, errors.New("emergency group can't change QuotaDuration"))
}
if len(errs) != 0 {
return errors.Join(errs...)
}
return nil
}

// SetIBCStatus update the ibc-transfer status in module params.
Expand Down
39 changes: 39 additions & 0 deletions x/uibc/quota/keeper/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package keeper

import (
"testing"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/umee-network/umee/v6/x/uibc"
)
Expand All @@ -26,3 +28,40 @@ func TestUnitParams(t *testing.T) {
params2 := k.GetParams()
require.Equal(params, params2)
}

func TestValidateEmergencyQuotaParamsUpdate(t *testing.T) {
mkParams := func(total, token int64, duration time.Duration) uibc.Params {
return uibc.Params{
TotalQuota: sdk.NewDec(total),
TokenQuota: sdk.NewDec(token),
QuotaDuration: duration,
}
}

p := mkParams(100, 10, 50)
tcs := []struct {
name string
p uibc.Params
errMsg string
}{
{"no change", p, ""},
{"valid total quota update", mkParams(99, 10, 50), ""},
{"valid update", mkParams(0, 0, 50), ""},

{"invalid update", mkParams(201, 11, 50), "can't increase"},
{"invalid total quota update", mkParams(101, 10, 50), "can't increase"},
{"invalid token quota update", mkParams(10, 12, 50), "can't increase"},
{"invalid quota duration update1", mkParams(100, 10, 51), "can't change QuotaDuration"},
{"invalid quota duration update2", mkParams(100, 10, 49), "can't change QuotaDuration"},
}

assert := assert.New(t)
for _, tc := range tcs {
err := validateEmergencyQuotaParamsUpdate(p, tc.p)
if tc.errMsg == "" {
assert.NoError(err, tc.name)
} else {
assert.ErrorContains(err, tc.errMsg, tc.name)
}
}
}
4 changes: 3 additions & 1 deletion x/uibc/quota/keeper/unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/umee-network/umee/v6/tests/tsdk"
ugovmocks "github.com/umee-network/umee/v6/x/ugov/mocks"
"github.com/umee-network/umee/v6/x/uibc"
)

Expand All @@ -20,10 +21,11 @@ const (

// creates keeper without external dependencies (app, leverage etc...)
func initKeeper(t *testing.T, l uibc.Leverage, o uibc.Oracle) TestKeeper {
eg := ugovmocks.NewSimpleEmergencyGroupBuilder()
ir := cdctypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(ir)
storeKey := storetypes.NewMemoryStoreKey("quota")
kb := NewKeeperBuilder(cdc, storeKey, l, o)
kb := NewKeeperBuilder(cdc, storeKey, l, o, eg)
ctx, _ := tsdk.NewCtxOneStore(t, storeKey)
return TestKeeper{kb.Keeper(&ctx), t, &ctx}
}
Expand Down

0 comments on commit 5efdbd4

Please sign in to comment.