Skip to content

Commit

Permalink
Release/v1.2.x (#282)
Browse files Browse the repository at this point in the history
* do not remove delegation records on absence from delegatordelegations callback
* add tests for delegations callback changes
* fix onboarding race condition with attempting to set withdrawal address on non-existent / unitialized ICA
* fix GetTxEvents pagination/sort issue
* test fixes
* ensure undelegate msg receipt triggers single validator requery, not entire set
* add Uncapped OverrideRedemptionRate function; add tests for capped and uncapped redemption rate calculations
  • Loading branch information
Joe Bowman authored Jan 18, 2023
1 parent eb48a81 commit 8941f1a
Show file tree
Hide file tree
Showing 8 changed files with 557 additions and 35 deletions.
8 changes: 8 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,14 @@ func (app *Quicksilver) Name() string { return app.BaseApp.Name() }

// BeginBlocker updates every begin block
func (app *Quicksilver) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
if ctx.ChainID() == "quicksilver-2" && ctx.BlockHeight() == 235001 {
zone, found := app.InterchainstakingKeeper.GetZone(ctx, "stargaze-1")
if !found {
panic("ERROR: unable to find expected stargaze-1 zone")
}
app.InterchainstakingKeeper.OverrideRedemptionRateNoCap(ctx, zone)
}

return app.mm.BeginBlock(ctx, req)
}

Expand Down
19 changes: 0 additions & 19 deletions x/interchainstaking/keeper/callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/bech32"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
querypb "github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/types/tx"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
Expand Down Expand Up @@ -226,23 +225,6 @@ func DepositIntervalCallback(k Keeper, ctx sdk.Context, args []byte, query icqty
return err
}

// TODO: use pagination.GetTotal() to dispatch the correct number of requests now; rather than iteratively.
if len(txs.Pagination.NextKey) > 0 {
req := tx.GetTxsEventRequest{}
if len(query.Request) == 0 {
return errors.New("attempted to unmarshal zero length byte slice (5)")
}
err := k.cdc.Unmarshal(query.Request, &req)
if err != nil {
return err
}
if req.Pagination == nil {
req.Pagination = new(querypb.PageRequest)
}
req.Pagination.Key = txs.Pagination.NextKey
k.ICQKeeper.MakeRequest(ctx, query.ConnectionId, query.ChainId, "cosmos.tx.v1beta1.Service/GetTxsEvent", k.cdc.MustMarshal(&req), sdk.NewInt(-1), types.ModuleName, "depositinterval", 0)
}

for _, txn := range txs.TxResponses {
req := tx.GetTxRequest{Hash: txn.TxHash}
hashBytes := k.cdc.MustMarshal(&req)
Expand All @@ -251,7 +233,6 @@ func DepositIntervalCallback(k Keeper, ctx sdk.Context, args []byte, query icqty
k.Logger(ctx).Info("Found previously handled tx. Ignoring.", "txhash", txn.TxHash)
continue
}
// k.HandleReceiptTransaction(ctx, txn, txs.Txs[idx], zone)
k.ICQKeeper.MakeRequest(ctx, query.ConnectionId, query.ChainId, "tendermint.Tx", hashBytes, sdk.NewInt(-1), types.ModuleName, "deposittx", 0)
}
return nil
Expand Down
360 changes: 360 additions & 0 deletions x/interchainstaking/keeper/callbacks_test.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions x/interchainstaking/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

Expand Down Expand Up @@ -59,7 +60,7 @@ func (k Keeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epochNumb
// OnChanOpenAck calls SetWithdrawalAddress (see ibc_module.go)
k.Logger(ctx).Info("Withdrawing rewards")

delegationQuery := stakingtypes.QueryDelegatorDelegationsRequest{DelegatorAddr: zoneInfo.DelegationAddress.Address}
delegationQuery := stakingtypes.QueryDelegatorDelegationsRequest{DelegatorAddr: zoneInfo.DelegationAddress.Address, Pagination: &query.PageRequest{Limit: uint64(len(zoneInfo.Validators))}}
bz := k.cdc.MustMarshal(&delegationQuery)

k.ICQKeeper.MakeRequest(
Expand All @@ -73,7 +74,6 @@ func (k Keeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epochNumb
"delegations",
0,
)
// zoneInfo.DelegationAddress.IncrementBalanceWaitgroup()

rewardsQuery := distrtypes.QueryDelegationTotalRewardsRequest{DelegatorAddress: zoneInfo.DelegationAddress.Address}
bz = k.cdc.MustMarshal(&rewardsQuery)
Expand Down
30 changes: 18 additions & 12 deletions x/interchainstaking/keeper/ibc_packet_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,20 +653,31 @@ func (k *Keeper) HandleUndelegate(ctx sdk.Context, msg sdk.Msg, completion time.
k.Logger(ctx).Info("withdrawal record to save", "rcd", record)
k.UpdateWithdrawalRecordStatus(ctx, &record, WithdrawStatusUnbond)
}
delegationQuery := stakingtypes.QueryDelegatorDelegationsRequest{DelegatorAddr: undelegateMsg.DelegatorAddress}
bz := k.cdc.MustMarshal(&delegationQuery)

delAddr, err := utils.AccAddressFromBech32(undelegateMsg.DelegatorAddress, "")
if err != nil {
return err
}
valAddr, err := utils.ValAddressFromBech32(undelegateMsg.ValidatorAddress, "")
if err != nil {
return err
}

data := stakingtypes.GetDelegationKey(delAddr, valAddr)

// send request to update delegation record for undelegated del/val tuple.
k.ICQKeeper.MakeRequest(
ctx,
zone.ConnectionId,
zone.ChainId,
"cosmos.staking.v1beta1.Query/DelegatorDelegations",
bz,
"store/staking/key",
data,
sdk.NewInt(-1),
types.ModuleName,
"delegations",
"delegation",
0,
)

return nil
}

Expand Down Expand Up @@ -809,11 +820,8 @@ func (k *Keeper) UpdateDelegationRecordsForAddress(ctx sdk.Context, zone types.Z
}
data := stakingtypes.GetDelegationKey(delAddr, valAddr)

if err := k.RemoveDelegation(ctx, &zone, existingDelegation); err != nil {
return err
}

// send request to prove delegation no longer exists.
// send request to prove delegation no longer exists. If the response is nil (i.e. no delegation), then
// the delegation record is removed by the callback.
k.ICQKeeper.MakeRequest(
ctx,
zone.ConnectionId,
Expand All @@ -827,8 +835,6 @@ func (k *Keeper) UpdateDelegationRecordsForAddress(ctx sdk.Context, zone types.Z
)
}

// k.SetZone(ctx, &zone)

return nil
}

Expand Down
12 changes: 11 additions & 1 deletion x/interchainstaking/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ func (k Keeper) depositInterval(ctx sdk.Context) zoneItrFn {
if !zoneInfo.DepositAddress.Balance.Empty() {
k.Logger(ctx).Info("balance is non zero", "balance", zoneInfo.DepositAddress.Balance)

req := tx.GetTxsEventRequest{Events: []string{"transfer.recipient='" + zoneInfo.DepositAddress.GetAddress() + "'"}, Pagination: &query.PageRequest{Limit: types.TxRetrieveCount, Reverse: true}}
req := tx.GetTxsEventRequest{Events: []string{"transfer.recipient='" + zoneInfo.DepositAddress.GetAddress() + "'"}, OrderBy: tx.OrderBy_ORDER_BY_DESC, Pagination: &query.PageRequest{Limit: types.TxRetrieveCount}}
k.ICQKeeper.MakeRequest(ctx, zoneInfo.ConnectionId, zoneInfo.ChainId, "cosmos.tx.v1beta1.Service/GetTxsEvent", k.cdc.MustMarshal(&req), sdk.NewInt(-1), types.ModuleName, "depositinterval", 0)

}
Expand Down Expand Up @@ -440,6 +440,16 @@ func (k *Keeper) UpdateRedemptionRate(ctx sdk.Context, zone types.Zone, epochRew
k.SetZone(ctx, &zone)
}

func (k *Keeper) OverrideRedemptionRateNoCap(ctx sdk.Context, zone types.Zone) {
ratio, _ := k.GetRatio(ctx, zone, sdk.ZeroInt())
k.Logger(ctx).Info("Last redemption rate", "rate", zone.LastRedemptionRate)
k.Logger(ctx).Info("Current redemption rate", "rate", zone.RedemptionRate)
k.Logger(ctx).Info("New redemption rate", "rate", ratio, "supply", k.BankKeeper.GetSupply(ctx, zone.LocalDenom).Amount, "lv", k.GetDelegatedAmount(ctx, &zone).Amount)

zone.RedemptionRate = ratio
k.SetZone(ctx, &zone)
}

func (k *Keeper) GetRatio(ctx sdk.Context, zone types.Zone, epochRewards math.Int) (sdk.Dec, bool) {
// native asset amount
nativeAssetAmount := k.GetDelegatedAmount(ctx, &zone).Amount
Expand Down
152 changes: 152 additions & 0 deletions x/interchainstaking/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,3 +491,155 @@ func (s *KeeperTestSuite) TestGetRatio() {
})
}
}

func (s *KeeperTestSuite) TestUpdateRedemptionRate() {
s.SetupTest()
s.setupTestZones()

app := s.GetQuicksilverApp(s.chainA)
ctx := s.chainA.GetContext()
icsKeeper := app.InterchainstakingKeeper
zone, found := icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)

vals := s.GetQuicksilverApp(s.chainB).StakingKeeper.GetAllValidators(s.chainB.GetContext())
delegationA := icstypes.Delegation{DelegationAddress: zone.DelegationAddress.Address, ValidatorAddress: vals[0].OperatorAddress, Amount: sdk.NewCoin(zone.BaseDenom, sdk.NewInt(1000))}
delegationB := icstypes.Delegation{DelegationAddress: zone.DelegationAddress.Address, ValidatorAddress: vals[1].OperatorAddress, Amount: sdk.NewCoin(zone.BaseDenom, sdk.NewInt(1000))}
delegationC := icstypes.Delegation{DelegationAddress: zone.DelegationAddress.Address, ValidatorAddress: vals[2].OperatorAddress, Amount: sdk.NewCoin(zone.BaseDenom, sdk.NewInt(1000))}

icsKeeper.SetDelegation(ctx, &zone, delegationA)
icsKeeper.SetDelegation(ctx, &zone, delegationB)
icsKeeper.SetDelegation(ctx, &zone, delegationC)

app.MintKeeper.MintCoins(ctx, sdk.NewCoins(sdk.NewCoin(zone.LocalDenom, sdk.NewInt(3000))))

// no change!
s.Require().Equal(sdk.OneDec(), zone.RedemptionRate)
icsKeeper.UpdateRedemptionRate(ctx, zone, sdk.ZeroInt())

zone, found = icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)
s.Require().Equal(sdk.OneDec(), zone.RedemptionRate)

// add 1%
s.Require().Equal(sdk.OneDec(), zone.RedemptionRate)
icsKeeper.UpdateRedemptionRate(ctx, zone, sdk.NewInt(30))
delegationA.Amount.Amount = delegationA.Amount.Amount.AddRaw(10)
delegationB.Amount.Amount = delegationB.Amount.Amount.AddRaw(10)
delegationC.Amount.Amount = delegationC.Amount.Amount.AddRaw(10)
icsKeeper.SetDelegation(ctx, &zone, delegationA)
icsKeeper.SetDelegation(ctx, &zone, delegationB)
icsKeeper.SetDelegation(ctx, &zone, delegationC)

zone, found = icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)
s.Require().Equal(sdk.NewDecWithPrec(101, 2), zone.RedemptionRate)

// add >2%; cap at 2%
icsKeeper.UpdateRedemptionRate(ctx, zone, sdk.NewInt(500))
delegationA.Amount.Amount = delegationA.Amount.Amount.AddRaw(166)
delegationB.Amount.Amount = delegationB.Amount.Amount.AddRaw(167)
delegationC.Amount.Amount = delegationC.Amount.Amount.AddRaw(167)
icsKeeper.SetDelegation(ctx, &zone, delegationA)
icsKeeper.SetDelegation(ctx, &zone, delegationB)
icsKeeper.SetDelegation(ctx, &zone, delegationC)
zone, found = icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)
// should be capped at 2% increase. (1.01*1.02 == 1.0302)
s.Require().Equal(sdk.NewDecWithPrec(10302, 4), zone.RedemptionRate)

// add nothing, still cap at 2%
icsKeeper.UpdateRedemptionRate(ctx, zone, sdk.ZeroInt())
zone, found = icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)
// should be capped at 2% increase. (1.01*1.02*1.02 == 1.050804)
s.Require().Equal(sdk.NewDecWithPrec(1050804, 6), zone.RedemptionRate)

delegationA.Amount.Amount = delegationA.Amount.Amount.SubRaw(500)
delegationB.Amount.Amount = delegationB.Amount.Amount.SubRaw(500)
delegationC.Amount.Amount = delegationC.Amount.Amount.SubRaw(500)
icsKeeper.SetDelegation(ctx, &zone, delegationA)
icsKeeper.SetDelegation(ctx, &zone, delegationB)
icsKeeper.SetDelegation(ctx, &zone, delegationC)

// remove > 5%, cap at -5%
icsKeeper.UpdateRedemptionRate(ctx, zone, sdk.ZeroInt())
zone, found = icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)

s.Require().Equal(sdk.NewDecWithPrec(9982638, 7), zone.RedemptionRate)
}

func (s *KeeperTestSuite) TestOverrideRedemptionRateNoCap() {
s.SetupTest()
s.setupTestZones()

app := s.GetQuicksilverApp(s.chainA)
ctx := s.chainA.GetContext()
icsKeeper := app.InterchainstakingKeeper
zone, found := icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)

vals := s.GetQuicksilverApp(s.chainB).StakingKeeper.GetAllValidators(s.chainB.GetContext())
delegationA := icstypes.Delegation{DelegationAddress: zone.DelegationAddress.Address, ValidatorAddress: vals[0].OperatorAddress, Amount: sdk.NewCoin(zone.BaseDenom, sdk.NewInt(1000))}
delegationB := icstypes.Delegation{DelegationAddress: zone.DelegationAddress.Address, ValidatorAddress: vals[1].OperatorAddress, Amount: sdk.NewCoin(zone.BaseDenom, sdk.NewInt(1000))}
delegationC := icstypes.Delegation{DelegationAddress: zone.DelegationAddress.Address, ValidatorAddress: vals[2].OperatorAddress, Amount: sdk.NewCoin(zone.BaseDenom, sdk.NewInt(1000))}

icsKeeper.SetDelegation(ctx, &zone, delegationA)
icsKeeper.SetDelegation(ctx, &zone, delegationB)
icsKeeper.SetDelegation(ctx, &zone, delegationC)

app.MintKeeper.MintCoins(ctx, sdk.NewCoins(sdk.NewCoin(zone.LocalDenom, sdk.NewInt(3000))))

// no change!
s.Require().Equal(sdk.OneDec(), zone.RedemptionRate)
icsKeeper.OverrideRedemptionRateNoCap(ctx, zone)

zone, found = icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)
s.Require().Equal(sdk.OneDec(), zone.RedemptionRate)

// add 1%
delegationA.Amount.Amount = delegationA.Amount.Amount.AddRaw(10)
delegationB.Amount.Amount = delegationB.Amount.Amount.AddRaw(10)
delegationC.Amount.Amount = delegationC.Amount.Amount.AddRaw(10)
icsKeeper.SetDelegation(ctx, &zone, delegationA)
icsKeeper.SetDelegation(ctx, &zone, delegationB)
icsKeeper.SetDelegation(ctx, &zone, delegationC)
icsKeeper.OverrideRedemptionRateNoCap(ctx, zone)

zone, found = icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)
s.Require().Equal(sdk.NewDecWithPrec(101, 2), zone.RedemptionRate)

// add >2%; no cap
delegationA.Amount.Amount = delegationA.Amount.Amount.AddRaw(166)
delegationB.Amount.Amount = delegationB.Amount.Amount.AddRaw(167)
delegationC.Amount.Amount = delegationC.Amount.Amount.AddRaw(167)
icsKeeper.SetDelegation(ctx, &zone, delegationA)
icsKeeper.SetDelegation(ctx, &zone, delegationB)
icsKeeper.SetDelegation(ctx, &zone, delegationC)
icsKeeper.OverrideRedemptionRateNoCap(ctx, zone)

zone, found = icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)
s.Require().Equal(sdk.NewDecWithPrec(1176666666666666667, 18), zone.RedemptionRate)

// add nothing, no change
icsKeeper.OverrideRedemptionRateNoCap(ctx, zone)
zone, found = icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)
s.Require().Equal(sdk.NewDecWithPrec(1176666666666666667, 18), zone.RedemptionRate)

delegationA.Amount.Amount = delegationA.Amount.Amount.SubRaw(500)
delegationB.Amount.Amount = delegationB.Amount.Amount.SubRaw(500)
delegationC.Amount.Amount = delegationC.Amount.Amount.SubRaw(500)
icsKeeper.SetDelegation(ctx, &zone, delegationA)
icsKeeper.SetDelegation(ctx, &zone, delegationB)
icsKeeper.SetDelegation(ctx, &zone, delegationC)
icsKeeper.OverrideRedemptionRateNoCap(ctx, zone)
zone, found = icsKeeper.GetZone(ctx, s.chainB.ChainID)
s.Require().True(found)

s.Require().Equal(sdk.NewDecWithPrec(676666666666666667, 18), zone.RedemptionRate)
}
7 changes: 6 additions & 1 deletion x/interchainstaking/keeper/zones.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,14 @@ func (k *Keeper) EnsureWithdrawalAddresses(ctx sdk.Context, zone *types.Zone) er
k.Logger(ctx).Info("Delegation address not set")
return nil
}

if zone.DepositAddress == nil {
k.Logger(ctx).Info("Deposit address not set")
return nil
}
withdrawalAddress := zone.WithdrawalAddress.Address

if zone.DepositAddress.WithdrawalAddress != zone.DepositAddress.Address {
if zone.DepositAddress.WithdrawalAddress != zone.WithdrawalAddress.Address {
msg := distrTypes.MsgSetWithdrawAddress{DelegatorAddress: zone.DepositAddress.Address, WithdrawAddress: withdrawalAddress}
err := k.SubmitTx(ctx, []sdk.Msg{&msg}, zone.DepositAddress, "")
if err != nil {
Expand Down

0 comments on commit 8941f1a

Please sign in to comment.