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

batching delegations and undelegations #1263

Merged
merged 46 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
10a1abd
Batched delegations (#1201)
sampocs Jun 13, 2024
74c51cb
batched undelegations (#1195)
sampocs Jun 13, 2024
99d773f
staketia migration (#1212)
sampocs Jun 13, 2024
a5b31a6
fixed unit tests
sampocs Jun 13, 2024
1582770
V23 upgrade handler (batching and stTIA) (#1219)
sampocs Jun 13, 2024
95f12fd
added TODO
sampocs Jun 14, 2024
cd8d976
[AUDIT FIX] - short circuit callback on completed records
sampocs Jun 14, 2024
def6f4a
[AUDIT FIX] - decrement remaining delegated balance in redeem stake
sampocs Jun 20, 2024
f1fe92c
[AUDIT FIX] - set min/max inner redemption rate after migration
sampocs Jun 20, 2024
df38a31
[AUDIT FIX] - updated total delegations in confirm delegation
sampocs Jun 20, 2024
c3ffbf6
Merge branch 'main' into v23
sampocs Jun 20, 2024
25ed61b
[AUDIT FIX] migrate halted field, remove prepare delegation, clarifyi…
sampocs Jun 29, 2024
66041f3
[AUDIT FIX] decrement total delegations in adjust delegated balance
sampocs Jul 9, 2024
82a007b
[AUDIT FIX] enable stakeibc redemptions with if exact remaining amoun…
sampocs Jul 9, 2024
7e4a9a9
[AUDIT FIX] set delegation/undelegationTxsInProgress in migration
sampocs Jul 10, 2024
07d8a83
reset delegation txs in progress to 0 in restore-ica
sampocs Jul 18, 2024
639777d
addressed riley and vishal pr comments
sampocs Jul 26, 2024
38555eb
updated package from v22 -> v23
sampocs Jul 26, 2024
81f7cc9
generated protos
sampocs Jul 26, 2024
68deb54
moved upgrade to v24
sampocs Jul 26, 2024
8fdf6c6
removed trade route migration from v24 upgrade
sampocs Jul 26, 2024
d35a8e8
Merge branch 'main' into v23
sampocs Jul 26, 2024
72b9b41
fixed unit tests
sampocs Jul 26, 2024
13288dd
copied over the redemption rate update functions into staketia migration
sampocs Jul 26, 2024
ec24683
fixed build errors and updated RR functions
sampocs Jul 26, 2024
046f992
added invariant from RR change
sampocs Jul 26, 2024
5c79959
removed debug logs
sampocs Jul 26, 2024
07f1411
fixed unit test
sampocs Jul 26, 2024
c9b390d
nit
sampocs Jul 26, 2024
059843d
moved up RR invariant check and removed balance check
sampocs Aug 6, 2024
a57552d
nit, used GetActiveHostZone and moved epoch tracker
sampocs Aug 6, 2024
f5574b1
fixed unit tests
sampocs Aug 6, 2024
19e3617
nit: fixed comment typo in icacallbacks_undelegate.go
sampocs Aug 7, 2024
b58fea0
Merge branch 'main' into v23
assafmo Aug 8, 2024
afa4bfb
Merge branch 'main' into v23
sampocs Aug 16, 2024
308de94
reverted all stTIA migration changes
sampocs Aug 16, 2024
79a0028
removed net new files from sttia
sampocs Aug 16, 2024
6d39b4e
removed staketia migration call from upgrade handler
sampocs Aug 16, 2024
3db83e2
added full upgrade handler test
sampocs Aug 16, 2024
3a3745f
moved redemption sweep to stride epoch (#1265)
sampocs Aug 16, 2024
c176fe6
fixed unit tests
sampocs Aug 23, 2024
af9debb
fixed lint errors in claim/utils/icacallbacks/mint/icaoracle/intercha…
sampocs Aug 23, 2024
ad10e1e
fixed lint errors in icacallbacks
sampocs Aug 23, 2024
a0ef012
fixed lint errors in the rest of stakeibc
sampocs Aug 23, 2024
c3642aa
fixed lint errors in upgrades, autopilot, icacallbacks, and mint
sampocs Aug 26, 2024
90d4085
addressed riley pr comments
sampocs Aug 27, 2024
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
41 changes: 15 additions & 26 deletions x/stakeibc/keeper/claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,13 @@ func (k msgServer) ClaimUndelegatedTokens(goCtx context.Context, msg *types.MsgC
k.Logger(ctx).Info(fmt.Sprintf("ClaimUndelegatedTokens %v", msg))
userRedemptionRecord, err := k.GetClaimableRedemptionRecord(ctx, msg)
if err != nil {
errMsg := fmt.Sprintf("unable to find claimable redemption record for msg: %v, error %s", msg, err.Error())
k.Logger(ctx).Error(errMsg)
return nil, errorsmod.Wrap(types.ErrRecordNotFound, errMsg)
return nil, errorsmod.Wrapf(err, "unable to find claimable redemption record for msg %+v", msg)
}

hostZone, found := k.GetHostZone(ctx, msg.HostZoneId)
if !found {
return nil, errorsmod.Wrap(types.ErrHostZoneNotFound, fmt.Sprintf("Host zone %s not found", msg.HostZoneId))
}

if hostZone.Halted {
k.Logger(ctx).Error(fmt.Sprintf("Host Zone %s halted", msg.HostZoneId))
return nil, errorsmod.Wrapf(types.ErrHaltedHostZone, "Host Zone %s halted", msg.HostZoneId)
// Confirm host zone is not halted
_, err = k.GetActiveHostZone(ctx, msg.HostZoneId)
if err != nil {
return nil, err
}

icaTx, err := k.GetRedemptionTransferMsg(ctx, userRedemptionRecord, msg.HostZoneId)
Expand Down Expand Up @@ -75,29 +69,26 @@ func (k Keeper) GetClaimableRedemptionRecord(ctx sdk.Context, msg *types.MsgClai
userRedemptionRecordKey := recordstypes.UserRedemptionRecordKeyFormatter(msg.HostZoneId, msg.Epoch, msg.Receiver)
userRedemptionRecord, found := k.RecordsKeeper.GetUserRedemptionRecord(ctx, userRedemptionRecordKey)
if !found {
errMsg := fmt.Sprintf("User redemption record %s not found on host zone %s", userRedemptionRecordKey, msg.HostZoneId)
k.Logger(ctx).Error(errMsg)
return nil, errorsmod.Wrap(types.ErrInvalidUserRedemptionRecord, errMsg)
return nil, errorsmod.Wrapf(types.ErrInvalidUserRedemptionRecord,
"user redemption record %s not found on host zone %s", userRedemptionRecordKey, msg.HostZoneId)
}

// check that the record is claimable
hostZoneUnbonding, found := k.RecordsKeeper.GetHostZoneUnbondingByChainId(ctx, userRedemptionRecord.EpochNumber, msg.HostZoneId)
if !found {
errMsg := fmt.Sprintf("Host zone unbonding record %s not found on host zone %s", userRedemptionRecordKey, msg.HostZoneId)
k.Logger(ctx).Error(errMsg)
return nil, errorsmod.Wrapf(types.ErrInvalidUserRedemptionRecord, errMsg)
return nil, errorsmod.Wrapf(types.ErrInvalidUserRedemptionRecord,
"host zone unbonding record %s not found on host zone %s", userRedemptionRecordKey, msg.HostZoneId)
}
// records associated with host zone unbondings are claimable after the host zone unbonding tokens have been CLAIMABLE to the redemption account
if hostZoneUnbonding.Status != recordstypes.HostZoneUnbonding_CLAIMABLE {
errMsg := fmt.Sprintf("User redemption record %s is not claimable, host zone unbonding has status: %s, requires status CLAIMABLE", userRedemptionRecord.Id, hostZoneUnbonding.Status)
k.Logger(ctx).Error(errMsg)
return nil, errorsmod.Wrapf(types.ErrInvalidUserRedemptionRecord, errMsg)
return nil, errorsmod.Wrapf(types.ErrInvalidUserRedemptionRecord,
"user redemption record %s is not claimable, host zone unbonding has status: %s, requires status CLAIMABLE",
userRedemptionRecord.Id, hostZoneUnbonding.Status)
}
// records that have claimIsPending set to True have already been claimed (and are pending an ack)
if userRedemptionRecord.ClaimIsPending {
errMsg := fmt.Sprintf("User redemption record %s is not claimable, pending ack", userRedemptionRecord.Id)
k.Logger(ctx).Error(errMsg)
return nil, errorsmod.Wrapf(types.ErrInvalidUserRedemptionRecord, errMsg)
return nil, errorsmod.Wrapf(types.ErrInvalidUserRedemptionRecord,
"user redemption record %s is not claimable, pending ack", userRedemptionRecord.Id)
}
return &userRedemptionRecord, nil
}
Expand All @@ -124,9 +115,7 @@ func (k Keeper) GetRedemptionTransferMsg(ctx sdk.Context, userRedemptionRecord *
// TODO [optimization]: Remove reference to epoch time (make timeout relative to block time)
epochTracker, found := k.GetEpochTracker(ctx, epochstypes.STRIDE_EPOCH)
if !found {
errMsg := fmt.Sprintf("Epoch tracker not found for epoch %s", epochstypes.STRIDE_EPOCH)
k.Logger(ctx).Error(errMsg)
return nil, errorsmod.Wrap(types.ErrEpochNotFound, errMsg)
return nil, errorsmod.Wrapf(types.ErrEpochNotFound, "epoch tracker not found for epoch %s", epochstypes.STRIDE_EPOCH)
}
icaTimeOutNanos := k.GetParam(ctx, types.KeyICATimeoutNanos)
nextEpochStarttime := epochTracker.NextEpochStartTime
Expand Down
12 changes: 5 additions & 7 deletions x/stakeibc/keeper/claim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (s *KeeperTestSuite) TestClaimUndelegatedTokens_NoUserRedemptionRecord() {
s.App.RecordsKeeper.RemoveUserRedemptionRecord(s.Ctx, tc.initialState.redemptionRecordId)

_, err := s.GetMsgServer().ClaimUndelegatedTokens(sdk.WrapSDKContext(s.Ctx), &tc.validMsg)
s.Require().ErrorContains(err, "user redemption record error: record not found")
s.Require().ErrorContains(err, "unable to find claimable redemption record")
}

func (s *KeeperTestSuite) TestClaimUndelegatedTokens_RecordNotClaimable() {
Expand All @@ -144,7 +144,7 @@ func (s *KeeperTestSuite) TestClaimUndelegatedTokens_RecordNotClaimable() {
s.App.RecordsKeeper.SetUserRedemptionRecord(s.Ctx, alreadyClaimedRedemptionRecord)

_, err := s.GetMsgServer().ClaimUndelegatedTokens(sdk.WrapSDKContext(s.Ctx), &tc.validMsg)
s.Require().ErrorContains(err, "User redemption record GAIA.1.cosmos_RECEIVER is not claimable")
s.Require().ErrorContains(err, "user redemption record GAIA.1.cosmos_RECEIVER is not claimable")
}

func (s *KeeperTestSuite) TestClaimUndelegatedTokens_RecordNotFound() {
Expand All @@ -154,7 +154,7 @@ func (s *KeeperTestSuite) TestClaimUndelegatedTokens_RecordNotFound() {
invalidMsg.HostZoneId = "fake_host_zone"

_, err := s.GetMsgServer().ClaimUndelegatedTokens(sdk.WrapSDKContext(s.Ctx), &invalidMsg)
s.Require().ErrorContains(err, "User redemption record fake_host_zone.1.cosmos_RECEIVER not found on host zone fake_host_zone")
s.Require().ErrorContains(err, "user redemption record fake_host_zone.1.cosmos_RECEIVER not found on host zone fake_host_zone")
}

func (s *KeeperTestSuite) TestClaimUndelegatedTokens_HostZoneNotFound() {
Expand All @@ -180,17 +180,15 @@ func (s *KeeperTestSuite) TestClaimUndelegatedTokens_NoRedemptionAccount() {
s.App.StakeibcKeeper.SetHostZone(s.Ctx, hostZone)

_, err := s.App.StakeibcKeeper.GetRedemptionTransferMsg(s.Ctx, &tc.initialState.redemptionRecord, tc.validMsg.HostZoneId)
s.Require().EqualError(err, "Redemption account not found for host zone GAIA: ICA acccount not found on host zone")
s.Require().ErrorContains(err, "Redemption account not found for host zone GAIA")
}

func (s *KeeperTestSuite) TestClaimUndelegatedTokens_NoEpochTracker() {
tc := s.SetupClaimUndelegatedTokens()
s.App.StakeibcKeeper.RemoveEpochTracker(s.Ctx, epochtypes.STRIDE_EPOCH)

_, err := s.GetMsgServer().ClaimUndelegatedTokens(sdk.WrapSDKContext(s.Ctx), &tc.validMsg)
expectedErr := "unable to build redemption transfer message: "
expectedErr += "Epoch tracker not found for epoch stride_epoch: epoch not found"
s.Require().EqualError(err, expectedErr)
s.Require().ErrorContains(err, "epoch tracker not found for epoch stride_epoch")
}

func (s *KeeperTestSuite) TestClaimUndelegatedTokens_HzuNotStatusTransferred() {
Expand Down
8 changes: 4 additions & 4 deletions x/stakeibc/keeper/community_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (k Keeper) QueryCommunityPoolIcaBalance(
// Timeout query at end of epoch
strideEpochTracker, found := k.GetEpochTracker(ctx, epochstypes.STRIDE_EPOCH)
if !found {
return errorsmod.Wrapf(types.ErrEpochNotFound, epochstypes.STRIDE_EPOCH)
return errorsmod.Wrapf(types.ErrEpochNotFound, "epoch %s not found", epochstypes.STRIDE_EPOCH)
}
timeout := time.Unix(0, int64(strideEpochTracker.NextEpochStartTime))
timeoutDuration := timeout.Sub(ctx.BlockTime())
Expand Down Expand Up @@ -184,7 +184,7 @@ func (k Keeper) LiquidStakeCommunityPoolTokens(ctx sdk.Context, hostZone types.H
}
resp, err := msgServer.LiquidStake(ctx, &liquidStakeRequest)
if err != nil {
return types.ErrFailedToLiquidStake.Wrapf(err.Error())
return errorsmod.Wrap(err, "failed to liquid stake community pool tokens")
}

// If the liquid stake was successful, transfer the stTokens to the return ICA
Expand Down Expand Up @@ -220,7 +220,7 @@ func (k Keeper) RedeemCommunityPoolTokens(ctx sdk.Context, hostZone types.HostZo
Receiver: hostZone.CommunityPoolReturnIcaAddress,
}
if _, err := msgServer.RedeemStake(ctx, &redeemStakeRequest); err != nil {
return types.ErrUnableToRedeemStake.Wrapf(err.Error())
return errorsmod.Wrap(err, "failed to redeem community pool tokens")
}

return nil
Expand Down Expand Up @@ -286,7 +286,7 @@ func (k Keeper) FundCommunityPool(
// Timeout the ICA at the end of the epoch
strideEpochTracker, found := k.GetEpochTracker(ctx, epochstypes.STRIDE_EPOCH)
if !found {
return errorsmod.Wrapf(types.ErrEpochNotFound, epochstypes.STRIDE_EPOCH)
return errorsmod.Wrapf(types.ErrEpochNotFound, "epoch %s not found", epochstypes.STRIDE_EPOCH)
}
timeoutTimestamp := uint64(strideEpochTracker.NextEpochStartTime)

Expand Down
4 changes: 2 additions & 2 deletions x/stakeibc/keeper/community_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func (s *KeeperTestSuite) TestQueryCommunityPoolBalance_Failure_MissingEpoch() {
s.App.StakeibcKeeper.RemoveEpochTracker(s.Ctx, epochtypes.STRIDE_EPOCH)

err := s.App.StakeibcKeeper.QueryCommunityPoolIcaBalance(s.Ctx, tc.hostZone, icaAccountType, Atom)
s.Require().ErrorContains(err, "stride_epoch: epoch not found")
s.Require().ErrorContains(err, "epoch stride_epoch not found")
}

// Tests a community pool balance query that fails to submit the query
Expand Down Expand Up @@ -297,7 +297,7 @@ func (s *KeeperTestSuite) TestLiquidStakeCommunityPoolTokens_LiquidStakeFailure(
invalidHostZone.HostDenom = "invalid"

err := s.App.StakeibcKeeper.LiquidStakeCommunityPoolTokens(s.Ctx, invalidHostZone)
s.Require().ErrorContains(err, "Failed to liquid stake")
s.Require().ErrorContains(err, "failed to liquid stake community pool tokens")
}

// Set an invalid transfer channel on the host so that the transfer fails
Expand Down
21 changes: 6 additions & 15 deletions x/stakeibc/keeper/epoch_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,41 +100,32 @@ func (k Keeper) GetStrideEpochElapsedShare(ctx sdk.Context) (sdk.Dec, error) {
// Get the current stride epoch
epochTracker, found := k.GetEpochTracker(ctx, epochstypes.STRIDE_EPOCH)
if !found {
errMsg := fmt.Sprintf("Failed to get epoch tracker for %s", epochstypes.STRIDE_EPOCH)
k.Logger(ctx).Error(errMsg)
return sdk.ZeroDec(), errorsmod.Wrapf(sdkerrors.ErrNotFound, errMsg)
return sdk.ZeroDec(), errorsmod.Wrapf(sdkerrors.ErrNotFound, "Failed to get epoch tracker for %s", epochstypes.STRIDE_EPOCH)
}

// Get epoch start time, end time, and duration
epochDuration, err := cast.ToInt64E(epochTracker.Duration)
if err != nil {
errMsg := fmt.Sprintf("unable to convert epoch duration to int64, err: %s", err.Error())
k.Logger(ctx).Error(errMsg)
return sdk.ZeroDec(), errorsmod.Wrapf(types.ErrIntCast, errMsg)
return sdk.ZeroDec(), errorsmod.Wrap(err, "unable to convert epoch duration to int64")
}
epochEndTime, err := cast.ToInt64E(epochTracker.NextEpochStartTime)
if err != nil {
errMsg := fmt.Sprintf("unable to convert next epoch start time to int64, err: %s", err.Error())
k.Logger(ctx).Error(errMsg)
return sdk.ZeroDec(), errorsmod.Wrapf(types.ErrIntCast, errMsg)
return sdk.ZeroDec(), errorsmod.Wrapf(err, "unable to convert next epoch start time to int64")
sampocs marked this conversation as resolved.
Show resolved Hide resolved
}
epochStartTime := epochEndTime - epochDuration

// Confirm the current block time is inside the current epoch's start and end times
currBlockTime := ctx.BlockTime().UnixNano()
if currBlockTime < epochStartTime || currBlockTime > epochEndTime {
errMsg := fmt.Sprintf("current block time %d is not within current epoch (ending at %d)", currBlockTime, epochTracker.NextEpochStartTime)
k.Logger(ctx).Error(errMsg)
return sdk.ZeroDec(), errorsmod.Wrapf(types.ErrInvalidEpoch, errMsg)
return sdk.ZeroDec(), errorsmod.Wrapf(types.ErrInvalidEpoch,
"current block time %d is not within current epoch (ending at %d)", currBlockTime, epochTracker.NextEpochStartTime)
}

// Get elapsed share
elapsedTime := currBlockTime - epochStartTime
elapsedShare := sdk.NewDec(elapsedTime).Quo(sdk.NewDec(epochDuration))
if elapsedShare.LT(sdk.ZeroDec()) || elapsedShare.GT(sdk.OneDec()) {
errMsg := fmt.Sprintf("elapsed share (%s) for epoch is not between 0 and 1", elapsedShare)
k.Logger(ctx).Error(errMsg)
return sdk.ZeroDec(), errorsmod.Wrapf(types.ErrInvalidEpoch, errMsg)
return sdk.ZeroDec(), errorsmod.Wrapf(types.ErrInvalidEpoch, "elapsed share (%s) for epoch is not between 0 and 1", elapsedShare)
}

k.Logger(ctx).Info(fmt.Sprintf("Epoch elapsed share: %v (Block Time: %d, Epoch End Time: %d)", elapsedShare, currBlockTime, epochEndTime))
Expand Down
14 changes: 3 additions & 11 deletions x/stakeibc/keeper/epoch_tracker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package keeper_test

import (
"math"
"regexp"
"strconv"
"testing"

Expand Down Expand Up @@ -135,10 +134,7 @@ func (s *KeeperTestSuite) TestEpochElapsedShare_Failed_DurationOverflow() {
s.SetupEpochElapsedShares(maxDurationSeconds, DefaultNextStartTimeSeconds)

_, err := s.App.StakeibcKeeper.GetStrideEpochElapsedShare(s.Ctx)

expectedErrMsg := `unable to convert epoch duration to int64, err: overflow: `
expectedErrMsg += `unable to cast \d+ of type uint64 to int64: unable to cast to safe cast int`
s.Require().Regexp(regexp.MustCompile(expectedErrMsg), err.Error())
s.Require().ErrorContains(err, "unable to convert epoch duration to int64")
}

func (s *KeeperTestSuite) TestEpochElapsedShare_Failed_NextStartTimeOverflow() {
Expand All @@ -147,9 +143,7 @@ func (s *KeeperTestSuite) TestEpochElapsedShare_Failed_NextStartTimeOverflow() {
s.SetupEpochElapsedShares(DefaultEpochDurationSeconds, maxNextStartTimeSeconds)

_, err := s.App.StakeibcKeeper.GetStrideEpochElapsedShare(s.Ctx)
expectedErrMsg := `unable to convert next epoch start time to int64, err: overflow: `
expectedErrMsg += `unable to cast \d+ of type uint64 to int64: unable to cast to safe cast int`
s.Require().Regexp(regexp.MustCompile(expectedErrMsg), err.Error())
s.Require().ErrorContains(err, "unable to convert next epoch start time to int64")
}

func (s *KeeperTestSuite) TestEpochElapsedShare_Failed_CurrentBlockTimeOverflow() {
Expand All @@ -158,9 +152,7 @@ func (s *KeeperTestSuite) TestEpochElapsedShare_Failed_CurrentBlockTimeOverflow(
s.SetupEpochElapsedShares(DefaultEpochDurationSeconds, maxNextStartTimeSeconds)

_, err := s.App.StakeibcKeeper.GetStrideEpochElapsedShare(s.Ctx)
expectedErrMsg := `unable to convert next epoch start time to int64, err: overflow: `
expectedErrMsg += `unable to cast \d+ of type uint64 to int64: unable to cast to safe cast int`
s.Require().Regexp(regexp.MustCompile(expectedErrMsg), err.Error())
s.Require().ErrorContains(err, "unable to convert next epoch start time to int64")
}

func (s *KeeperTestSuite) TestEpochElapsedShare_Failed_BlockTimeOutsideEpoch() {
Expand Down
2 changes: 1 addition & 1 deletion x/stakeibc/keeper/host_zone.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ func (k Keeper) GetTargetValAmtsForHostZone(ctx sdk.Context, hostZone types.Host
func (k Keeper) EnableRedemptions(ctx sdk.Context, chainId string) error {
hostZone, found := k.GetHostZone(ctx, chainId)
if !found {
return types.ErrHostZoneNotFound.Wrapf(chainId)
return types.ErrHostZoneNotFound.Wrap(chainId)
}

hostZone.RedemptionsEnabled = true
Expand Down
Loading