Reset periodic vesting accounts for v17 migration (#1225)

- Move logic for reseting periodic vesting accounts to utils package
This commit is contained in:
Levi Schoen 2022-04-26 09:17:15 -07:00 committed by GitHub
parent 39d6176080
commit a33a7b1166
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 398 additions and 4 deletions

View File

@ -24,7 +24,8 @@ func newCmdContext() context.Context {
config := app.MakeEncodingConfig()
clientCtx := client.Context{}.
WithCodec(config.Marshaler).
WithLegacyAmino(config.Amino)
WithLegacyAmino(config.Amino).
WithInterfaceRegistry(config.InterfaceRegistry)
ctx := context.Background()
ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx)
return ctx

View File

@ -0,0 +1,59 @@
package utils
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
v040vesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
)
// ResetPeriodicVestingAccount resets a periodic vesting account to a new start
// time. The account is modified in place, and vesting periods before the new
// start time are removed from the account.
func ResetPeriodicVestingAccount(vacc *v040vesting.PeriodicVestingAccount, startTime time.Time) {
currentPeriod := vacc.StartTime
newOriginalVesting := sdk.Coins{}
newStartTime := startTime.Unix()
newPeriods := v040vesting.Periods{}
for _, period := range vacc.VestingPeriods {
currentPeriod = currentPeriod + period.Length
// Periods less than the newStartTime are still vesting,
// so adjust their length and add them to the newPeriods
if newStartTime < currentPeriod {
// adjust the length of the first vesting period
// to be relative to the new start time
if len(newPeriods) == 0 {
period.Length = currentPeriod - newStartTime
}
newOriginalVesting = newOriginalVesting.Add(period.Amount...)
newPeriods = append(newPeriods, period)
}
}
// If the new original vesting amount is less than the delegated vesting amount, set delegated vesting
// to the new original vesting amount, and add the difference to the delegated free amount
for _, delegatedVestingCoin := range vacc.DelegatedVesting {
newDelegatedVestingCoin := sdk.NewCoin(delegatedVestingCoin.Denom, sdk.MinInt(delegatedVestingCoin.Amount, newOriginalVesting.AmountOf(delegatedVestingCoin.Denom)))
delegationAdjustment := delegatedVestingCoin.Sub(newDelegatedVestingCoin)
if !delegationAdjustment.IsZero() {
vacc.DelegatedVesting = vacc.DelegatedVesting.Sub(sdk.NewCoins(delegationAdjustment))
vacc.DelegatedFree = vacc.DelegatedFree.Add(delegationAdjustment)
}
}
// update vesting account
vacc.StartTime = newStartTime
vacc.OriginalVesting = newOriginalVesting
vacc.VestingPeriods = newPeriods
// ensure end time is >= start time
if vacc.StartTime >= vacc.EndTime {
vacc.EndTime = vacc.StartTime
}
}

View File

@ -0,0 +1,231 @@
package utils
import (
"testing"
"time"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
"github.com/stretchr/testify/assert"
)
func createVestingAccount(balance sdk.Coins, vestingStart time.Time, vestingPeriods vestingtypes.Periods) *vestingtypes.PeriodicVestingAccount {
key := secp256k1.GenPrivKey()
pub := key.PubKey()
addr := sdk.AccAddress(pub.Address())
acc := authtypes.NewBaseAccount(addr, pub, 1, 1)
originalVesting := sdk.NewCoins()
for _, vp := range vestingPeriods {
originalVesting = originalVesting.Add(vp.Amount...)
}
return vestingtypes.NewPeriodicVestingAccount(acc, originalVesting, vestingStart.Unix(), vestingPeriods)
}
func TestResetPeriodVestingAccount_NoVestingPeriods(t *testing.T) {
vestingStartTime := time.Now().Add(-1 * time.Hour)
vacc := createVestingAccount(sdk.Coins{}, vestingStartTime, vestingtypes.Periods{})
newVestingStartTime := vestingStartTime.Add(time.Hour)
ResetPeriodicVestingAccount(vacc, newVestingStartTime)
assert.Equal(t, sdk.Coins{}, vacc.OriginalVesting, "expected original vesting to be zero")
assert.Equal(t, newVestingStartTime.Unix(), vacc.StartTime, "expected vesting start time to be updated")
assert.Equal(t, newVestingStartTime.Unix(), vacc.EndTime, "expected vesting end time to be updated")
assert.Equal(t, []vestingtypes.Period{}, vacc.VestingPeriods, "expected vesting periods to be empty")
}
func TestResetPeriodVestingAccount_SingleVestingPeriod_Vested(t *testing.T) {
balance := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6)))
vestingStartTime := time.Now().Add(-30 * 24 * time.Hour) // 30 days in past
periods := vestingtypes.Periods{
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // 15 days (-15 days in past)
Amount: balance,
},
}
vacc := createVestingAccount(balance, vestingStartTime, periods)
newVestingStartTime := vestingStartTime.Add(30 * 24 * time.Hour)
ResetPeriodicVestingAccount(vacc, newVestingStartTime)
assert.Equal(t, sdk.Coins{}, vacc.OriginalVesting, "expected original vesting to be zero")
assert.Equal(t, newVestingStartTime.Unix(), vacc.StartTime, "expected vesting start time to be updated")
assert.Equal(t, newVestingStartTime.Unix(), vacc.EndTime, "expected vesting end time to be updated")
assert.Equal(t, []vestingtypes.Period{}, vacc.VestingPeriods, "expected vesting periods to be empty")
}
func TestResetPeriodVestingAccount_SingleVestingPeriod_Vesting(t *testing.T) {
balance := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6)))
vestingStartTime := time.Now().Add(-30 * 24 * time.Hour) // 30 days in past
periods := vestingtypes.Periods{
vestingtypes.Period{
Length: 45 * 24 * 60 * 60, // 45 days
Amount: balance,
},
}
vacc := createVestingAccount(balance, vestingStartTime, periods)
newVestingStartTime := vestingStartTime.Add(30 * 24 * time.Hour)
ResetPeriodicVestingAccount(vacc, newVestingStartTime)
// new period length 15 days
expectedEndtime := newVestingStartTime.Add(15 * 24 * time.Hour).Unix()
// new period length changed, amount unchanged
expectedPeriods := []vestingtypes.Period{
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // 15 days
Amount: balance,
},
}
assert.Equal(t, balance, vacc.OriginalVesting, "expected original vesting to be unchanged")
assert.Equal(t, newVestingStartTime.Unix(), vacc.StartTime, "expected vesting start time to be updated")
assert.Equal(t, expectedEndtime, vacc.EndTime, "expected vesting end time end at last period")
assert.Equal(t, expectedPeriods, vacc.VestingPeriods, "expected vesting periods to be updated")
}
func TestResetPeriodVestingAccount_SingleVestingPeriod_ExactStartTime(t *testing.T) {
balance := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6)))
vestingStartTime := time.Now().Add(-30 * 24 * time.Hour) // 30 days in past
periods := vestingtypes.Periods{
vestingtypes.Period{
Length: 30 * 24 * 60 * 60, // 30 days - exact on the start time
Amount: balance,
},
}
vacc := createVestingAccount(balance, vestingStartTime, periods)
newVestingStartTime := vestingStartTime.Add(30 * 24 * time.Hour)
ResetPeriodicVestingAccount(vacc, newVestingStartTime)
// new period length is 0
expectedEndtime := newVestingStartTime.Unix()
// new period length changed, amount unchanged
expectedPeriods := []vestingtypes.Period{}
assert.Equal(t, sdk.Coins{}, vacc.OriginalVesting, "expected original vesting to be unchanged")
assert.Equal(t, newVestingStartTime.Unix(), vacc.StartTime, "expected vesting start time to be updated")
assert.Equal(t, expectedEndtime, vacc.EndTime, "expected vesting end time end at last period")
assert.Equal(t, expectedPeriods, vacc.VestingPeriods, "expected vesting periods to be updated")
}
func TestResetPeriodVestingAccount_MultiplePeriods(t *testing.T) {
balance := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(4e6)))
vestingStartTime := time.Now().Add(-30 * 24 * time.Hour) // 30 days in past
periods := vestingtypes.Periods{
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // -15 days - vested
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // 0 days - exact on the start time
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // +15 days - vesting
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // +30 days - vesting
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
}
vacc := createVestingAccount(balance, vestingStartTime, periods)
newVestingStartTime := vestingStartTime.Add(30 * 24 * time.Hour)
ResetPeriodicVestingAccount(vacc, newVestingStartTime)
// new period length 15 days
expectedEndtime := newVestingStartTime.Add(30 * 24 * time.Hour).Unix()
// new period length changed, amount unchanged
expectedPeriods := []vestingtypes.Period{
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // 15 days
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // 15 days
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
}
assert.Equal(t, sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(2e6))), vacc.OriginalVesting, "expected original vesting to be updated")
assert.Equal(t, newVestingStartTime.Unix(), vacc.StartTime, "expected vesting start time to be updated")
assert.Equal(t, expectedEndtime, vacc.EndTime, "expected vesting end time end at last period")
assert.Equal(t, expectedPeriods, vacc.VestingPeriods, "expected vesting periods to be updated")
}
func TestResetPeriodVestingAccount_DelegatedVesting_GreaterThanVesting(t *testing.T) {
balance := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(3e6)))
vestingStartTime := time.Now().Add(-30 * 24 * time.Hour) // 30 days in past
periods := vestingtypes.Periods{
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // -15 days - vested
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // 0 days - exact on the start time
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // +15 days - vesting
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
}
vacc := createVestingAccount(balance, vestingStartTime, periods)
vacc.TrackDelegation(vestingStartTime, balance, balance)
newVestingStartTime := vestingStartTime.Add(30 * 24 * time.Hour)
ResetPeriodicVestingAccount(vacc, newVestingStartTime)
assert.Equal(t, sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(2e6))), vacc.DelegatedFree, "expected delegated free to be updated")
assert.Equal(t, sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))), vacc.DelegatedVesting, "expected delegated vesting to be updated")
}
func TestResetPeriodVestingAccount_DelegatedVesting_LessThanVested(t *testing.T) {
balance := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(3e6)))
vestingStartTime := time.Now().Add(-30 * 24 * time.Hour) // 30 days in past
periods := vestingtypes.Periods{
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // -15 days - vested
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // 0 days - exact on the start time
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
vestingtypes.Period{
Length: 15 * 24 * 60 * 60, // +15 days - vesting
Amount: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))),
},
}
vacc := createVestingAccount(balance, vestingStartTime, periods)
vacc.TrackDelegation(vestingStartTime, balance, sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))))
newVestingStartTime := vestingStartTime.Add(30 * 24 * time.Hour)
ResetPeriodicVestingAccount(vacc, newVestingStartTime)
assert.Equal(t, sdk.Coins(nil), vacc.DelegatedFree, "expected delegrated free to be unmodified")
assert.Equal(t, sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e6))), vacc.DelegatedVesting, "expected delegated vesting to be unmodified")
}

35
migrate/v0_17/cosmos.go Normal file
View File

@ -0,0 +1,35 @@
package v0_17
import (
"time"
"github.com/cosmos/cosmos-sdk/client"
v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040"
v040authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
)
func MigrateCosmosAppState(appState genutiltypes.AppMap, clientCtx client.Context, genesisTime time.Time) genutiltypes.AppMap {
appState = migrateV040(appState, clientCtx, genesisTime)
return appState
}
// reset periodic vesting data for accounts
func migrateV040(appState genutiltypes.AppMap, clientCtx client.Context, genesisTime time.Time) genutiltypes.AppMap {
setConfigIfUnsealed()
v040Codec := clientCtx.Codec
// reset periodic vesting data for accounts
if appState[v040auth.ModuleName] != nil {
// unmarshal relative source genesis application state
var authGenState v040authtypes.GenesisState
v040Codec.MustUnmarshalJSON(appState[v040auth.ModuleName], &authGenState)
// reset periodic vesting data for accounts
appState[v040auth.ModuleName] = v040Codec.MustMarshalJSON(MigrateAuthV040(authGenState, genesisTime, clientCtx))
}
return appState
}

View File

@ -0,0 +1,67 @@
package v0_17
import (
"time"
"github.com/cosmos/cosmos-sdk/client"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
v040auth "github.com/cosmos/cosmos-sdk/x/auth/types"
v040vesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
"github.com/kava-labs/kava/migrate/utils"
)
// MigrateAuthV040 resets all periodic vesting accounts for a given
// v40 cosmos auth module genesis state, returning a copy of the original state where all
// periodic vesting accounts have been zeroed out.
func MigrateAuthV040(authGenState v040auth.GenesisState, genesisTime time.Time, ctx client.Context) *v040auth.GenesisState {
var anyAccounts = make([]*codectypes.Any, len(authGenState.Accounts))
for i, anyAcc := range authGenState.Accounts {
// Only need to make modifications to vesting accounts
if anyAcc.TypeUrl != "/cosmos.vesting.v1beta1.PeriodicVestingAccount" {
anyAccounts[i] = anyAcc
continue
}
var acc v040auth.GenesisAccount
if err := ctx.InterfaceRegistry.UnpackAny(anyAcc, &acc); err != nil {
panic(err)
}
if vacc, ok := acc.(*v040vesting.PeriodicVestingAccount); ok {
vestingPeriods := make([]v040vesting.Period, len(vacc.VestingPeriods))
for j, period := range vacc.VestingPeriods {
vestingPeriods[j] = v040vesting.Period{
Length: period.Length,
Amount: period.Amount,
}
}
vacc := v040vesting.PeriodicVestingAccount{
BaseVestingAccount: vacc.BaseVestingAccount,
StartTime: vacc.StartTime,
VestingPeriods: vestingPeriods,
}
utils.ResetPeriodicVestingAccount(&vacc, genesisTime)
// If periodic vesting account has zero periods, convert back
// to a base account
if genesisTime.Unix() >= vacc.EndTime {
any, err := codectypes.NewAnyWithValue(vacc.BaseVestingAccount.BaseAccount)
if err != nil {
panic(err)
}
anyAccounts[i] = any
continue
}
// Convert back to any
any, err := codectypes.NewAnyWithValue(&vacc)
if err != nil {
panic(err)
}
anyAccounts[i] = any
}
}
return &v040auth.GenesisState{
Params: authGenState.Params,
Accounts: anyAccounts,
}
}

View File

@ -33,9 +33,10 @@ func Migrate(genDoc *tmtypes.GenesisDoc, ctx client.Context) (*tmtypes.GenesisDo
var appState genutiltypes.AppMap
var err error
if err := json.Unmarshal(genDoc.AppState, &appState); err != nil {
return nil, fmt.Errorf("failed to marchal app state from genesis doc: %w", err)
return nil, fmt.Errorf("failed to unmarshal app state from genesis doc: %w", err)
}
MigrateCosmosAppState(appState, ctx, GenesisTime)
migrateAppState(appState, ctx)
genDoc.AppState, err = json.Marshal(appState)

View File

@ -161,10 +161,10 @@
"delegated_vesting": [],
"end_time": "1665767828"
},
"start_time": "1642608000",
"start_time": "1652202000",
"vesting_periods": [
{
"length": "23159828",
"length": "13565828",
"amount": [{ "denom": "swp", "amount": "14791312" }]
}
]