Skip to content

Commit

Permalink
Fix v10.x state incompatability (#2268)
Browse files Browse the repository at this point in the history
* Revert "x/lock: Fix `ExtendLockup` API  (backport #1937) (#2030)"

This reverts commit bd32316.

* Revert "Refactor `lock` method (backport #1936) (#2029)"

This reverts commit d733f64.

* Update changelog

* Update changelog
  • Loading branch information
ValarDragon authored Aug 3, 2022
1 parent 1a5d120 commit 9e178a6
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 172 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## v10.2.0

In v10.1.0 a refactor did changed the ordering of certain function calls in a message execution.
The data read and written to in state were the same, but re-ordered.
This unknowingly caused gas_used incompatabilities, because you could out-of-gas error at any intermediate step, and end in a different gas_used quantity. cref: https://github.com/cosmos/cosmos-sdk/issues/12788

The v10.2 line reverts the gas limit state incompatabilities introduced due to function ordering.
Namely this reverts

* [#1937](https://github.com/osmosis-labs/osmosis/pull/1937) Change `lockupKeeper.ExtendLock` to take in lockID instead of the direct lock struct.
* [#2030](https://github.com/osmosis-labs/osmosis/pull/2030) Rename lockup keeper `ResetAllLocks` to `InitializeAllLocks` and `ResetAllSyntheticLocks` to `InitializeAllSyntheticLocks`.

## v10.1.1

#### Improvements
Expand Down
8 changes: 5 additions & 3 deletions x/lockup/keeper/admin_keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"time"

"github.com/osmosis-labs/osmosis/v10/x/lockup/keeper"
"github.com/osmosis-labs/osmosis/v10/x/lockup/types"

sdk "github.com/cosmos/cosmos-sdk/types"
)
Expand All @@ -13,10 +14,11 @@ func (suite *KeeperTestSuite) TestRelock() {

addr1 := sdk.AccAddress([]byte("addr1---------------"))
coins := sdk.Coins{sdk.NewInt64Coin("stake", 10)}
lock := types.NewPeriodLock(1, addr1, time.Second, suite.Ctx.BlockTime().Add(time.Second), coins)

// lock with balance
suite.FundAcc(addr1, coins)
lock, err := suite.App.LockupKeeper.CreateLock(suite.Ctx, addr1, coins, time.Second)
err := suite.App.LockupKeeper.Lock(suite.Ctx, lock)
suite.Require().NoError(err)

// lock with balance with same id
Expand All @@ -36,12 +38,12 @@ func (suite *KeeperTestSuite) BreakLock() {

addr1 := sdk.AccAddress([]byte("addr1---------------"))
coins := sdk.Coins{sdk.NewInt64Coin("stake", 10)}
lock := types.NewPeriodLock(1, addr1, time.Second, suite.Ctx.BlockTime().Add(time.Second), coins)

// lock with balance
suite.FundAcc(addr1, coins)

lock, err := suite.App.LockupKeeper.CreateLock(suite.Ctx, addr1, coins, time.Second)

err := suite.App.LockupKeeper.Lock(suite.Ctx, lock)
suite.Require().NoError(err)

// break lock
Expand Down
4 changes: 0 additions & 4 deletions x/lockup/keeper/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,3 @@ func (k Keeper) SyntheticCoins(coins sdk.Coins, suffix string) sdk.Coins {
func (k Keeper) GetCoinsFromLocks(locks []types.PeriodLock) sdk.Coins {
return k.getCoinsFromLocks(locks)
}

func (k Keeper) Lock(ctx sdk.Context, lock types.PeriodLock, tokensToLock sdk.Coins) error {
return k.lock(ctx, lock, tokensToLock)
}
110 changes: 61 additions & 49 deletions x/lockup/keeper/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,79 +66,100 @@ func (k Keeper) AddToExistingLock(ctx sdk.Context, owner sdk.AccAddress, coin sd
// Tokens locked are sent and kept in the module account.
// This method alters the lock state in store, thus we do a sanity check to ensure
// lock owner matches the given owner.
func (k Keeper) AddTokensToLockByID(ctx sdk.Context, lockID uint64, owner sdk.AccAddress, tokensToAdd sdk.Coin) (*types.PeriodLock, error) {
func (k Keeper) AddTokensToLockByID(ctx sdk.Context, lockID uint64, owner sdk.AccAddress, coin sdk.Coin) (*types.PeriodLock, error) {
lock, err := k.GetLockByID(ctx, lockID)
if err != nil {
return nil, err
}

if lock.GetOwner() != owner.String() {
return nil, types.ErrNotLockOwner
}

lock.Coins = lock.Coins.Add(tokensToAdd)
err = k.lock(ctx, *lock, sdk.NewCoins(tokensToAdd))
if err != nil {
return nil, err
}
if err := k.bk.SendCoinsFromAccountToModule(ctx, lock.OwnerAddress(), types.ModuleName, sdk.NewCoins(coin)); err != nil {
return nil, err
}

for _, synthlock := range k.GetAllSyntheticLockupsByLockup(ctx, lock.ID) {
k.accumulationStore(ctx, synthlock.SynthDenom).Increase(accumulationKey(synthlock.Duration), tokensToAdd.Amount)
err = k.addTokenToLock(ctx, lock, coin)
if err != nil {
return nil, err
}

if k.hooks == nil {
return lock, nil
}

k.hooks.AfterAddTokensToLock(ctx, lock.OwnerAddress(), lock.GetID(), sdk.NewCoins(tokensToAdd))

k.hooks.OnTokenLocked(ctx, lock.OwnerAddress(), lock.ID, sdk.Coins{coin}, lock.Duration, lock.EndTime)
return lock, nil
}

// addTokenToLock adds token to lock and modifies the state of the lock and the accumulation store.
func (k Keeper) addTokenToLock(ctx sdk.Context, lock *types.PeriodLock, coin sdk.Coin) error {
lock.Coins = lock.Coins.Add(coin)

err := k.setLock(ctx, *lock)
if err != nil {
return err
}

// modifications to accumulation store
k.accumulationStore(ctx, coin.Denom).Increase(accumulationKey(lock.Duration), coin.Amount)

// CONTRACT: lock will have synthetic lock only if it has a single coin
// accumulation store for its synthetic denom is increased if exists.
lockedCoin, err := lock.SingleCoin()
if err == nil {
for _, synthlock := range k.GetAllSyntheticLockupsByLockup(ctx, lock.ID) {
k.accumulationStore(ctx, synthlock.SynthDenom).Increase(accumulationKey(synthlock.Duration), sdk.NewCoins(coin).AmountOf(lockedCoin.Denom))
}
}

k.hooks.AfterAddTokensToLock(ctx, lock.OwnerAddress(), lock.GetID(), sdk.NewCoins(coin))

return nil
}

// CreateLock creates a new lock with the specified duration for the owner.
// Returns an error in the following conditions:
// - account does not have enough balance
func (k Keeper) CreateLock(ctx sdk.Context, owner sdk.AccAddress, coins sdk.Coins, duration time.Duration) (types.PeriodLock, error) {
ID := k.GetLastLockID(ctx) + 1
// unlock time is initially set without a value, gets set as unlock start time + duration
// when unlocking starts.
lock := types.NewPeriodLock(ID, owner, duration, time.Time{}, coins)
err := k.lock(ctx, lock, lock.Coins)
err := k.Lock(ctx, lock)
if err != nil {
return lock, err
}

// add lock refs into not unlocking queue
err = k.addLockRefs(ctx, lock)
if err != nil {
return lock, err
}

k.SetLastLockID(ctx, lock.ID)
return lock, nil
}

// lock is an internal utility to lock coins and set corresponding states.
// This is only called by either of the two possible entry points to lock tokens.
// 1. CreateLock
// 2. AddTokensToLockByID
func (k Keeper) lock(ctx sdk.Context, lock types.PeriodLock, tokensToLock sdk.Coins) error {
// Lock is a utility method to lock tokens into the module account. This method includes setting the
// lock within the state machine and increasing the value of accumulation store.
func (k Keeper) Lock(ctx sdk.Context, lock types.PeriodLock) error {
owner, err := sdk.AccAddressFromBech32(lock.Owner)
if err != nil {
return err
}
if err := k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, tokensToLock); err != nil {
if err := k.bk.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, lock.Coins); err != nil {
return err
}

// store lock object into the store
err = k.setLock(ctx, lock)
store := ctx.KVStore(k.storeKey)
bz, err := proto.Marshal(&lock)
if err != nil {
return err
}
store.Set(lockStoreKey(lock.ID), bz)

// add lock refs into not unlocking queue
err = k.addLockRefs(ctx, lock)
if err != nil {
return err
}

// add to accumulation store
for _, coin := range tokensToLock {
for _, coin := range lock.Coins {
k.accumulationStore(ctx, coin.Denom).Increase(accumulationKey(lock.Duration), coin.Amount)
}

Expand Down Expand Up @@ -353,16 +374,7 @@ func (k Keeper) unlockMaturedLockInternalLogic(ctx sdk.Context, lock types.Perio
// 2. Locks that are unlokcing are not allowed to change duration.
// 3. Locks that have synthetic lockup are not allowed to change.
// 4. Provided duration should be greater than the original duration.
func (k Keeper) ExtendLockup(ctx sdk.Context, lockID uint64, owner sdk.AccAddress, newDuration time.Duration) error {
lock, err := k.GetLockByID(ctx, lockID)
if err != nil {
return err
}

if lock.GetOwner() != owner.String() {
return types.ErrNotLockOwner
}

func (k Keeper) ExtendLockup(ctx sdk.Context, lock types.PeriodLock, newDuration time.Duration) error {
if lock.IsUnlocking() {
return fmt.Errorf("cannot edit unlocking lockup for lock %d", lock.ID)
}
Expand All @@ -372,15 +384,10 @@ func (k Keeper) ExtendLockup(ctx sdk.Context, lockID uint64, owner sdk.AccAddres
return fmt.Errorf("cannot edit lockup with synthetic lock %d", lock.ID)
}

// completely delete existing lock refs
err = k.deleteLockRefs(ctx, unlockingPrefix(lock.IsUnlocking()), *lock)
if err != nil {
return err
}
oldLock := lock

oldDuration := lock.GetDuration()
if newDuration != 0 {
if newDuration <= oldDuration {
if newDuration <= lock.Duration {
return fmt.Errorf("new duration should be greater than the original")
}

Expand All @@ -393,20 +400,25 @@ func (k Keeper) ExtendLockup(ctx sdk.Context, lockID uint64, owner sdk.AccAddres
lock.Duration = newDuration
}

// add lock refs with the new duration
err = k.addLockRefs(ctx, *lock)
// update lockup
err := k.deleteLockRefs(ctx, unlockingPrefix(oldLock.IsUnlocking()), oldLock)
if err != nil {
return err
}

err = k.setLock(ctx, *lock)
err = k.addLockRefs(ctx, lock)
if err != nil {
return err
}

err = k.setLock(ctx, lock)
if err != nil {
return err
}

k.hooks.OnLockupExtend(ctx,
lock.GetID(),
oldDuration,
oldLock.GetDuration(),
lock.GetDuration(),
)

Expand Down
Loading

0 comments on commit 9e178a6

Please sign in to comment.