Skip to content

Commit

Permalink
feat(app): add upgrade handlers (#1121)
Browse files Browse the repository at this point in the history
* feat: wip mainnet upgrade handler

* wip: add ecocredit state migrations

* implement recover funds from lost account

* chore: fix tests

* chore: add test

* chore: fix test

* Update app/stable_appconfig.go

* chore: cleanup

* chore: cleanup

* feat: add redwood migrations

* Update x/ecocredit/migrations/v3/patch.go

* feat: add redwood migration tests

* chore: revert core_test.go

* Update app/stable_appconfig.go

Co-authored-by: Ryan Christoffersen <12519942+ryanchristo@users.noreply.github.com>

* chore: review changes

* chore: add redwood upgrade handler

* Update app/stable_appconfig.go

Co-authored-by: Ryan Christoffersen <12519942+ryanchristo@users.noreply.github.com>

* Update app/stable_appconfig.go

* Update x/ecocredit/migrations/v3/patch_test.go

Co-authored-by: Cory <cjlevinson@gmail.com>

* Update x/ecocredit/migrations/v3/patch_test.go

Co-authored-by: Cory <cjlevinson@gmail.com>

* Update x/ecocredit/migrations/v3/patch_test.go

Co-authored-by: Cory <cjlevinson@gmail.com>

* Update x/ecocredit/migrations/v3/patch_test.go

Co-authored-by: Cory <cjlevinson@gmail.com>

* chore: review changes

* chore: fix errors

* Update x/ecocredit/migrations/v3/patch.go

Co-authored-by: Cory <cjlevinson@gmail.com>

* update reference id comments and checks

Co-authored-by: Ryan Christoffersen <12519942+ryanchristo@users.noreply.github.com>
Co-authored-by: Anil Kumar Kammari <anil@vitwit.com>
Co-authored-by: Cory <cjlevinson@gmail.com>
  • Loading branch information
4 people authored Jun 3, 2022
1 parent abd6de2 commit 1abcee8
Show file tree
Hide file tree
Showing 10 changed files with 1,248 additions and 12 deletions.
97 changes: 97 additions & 0 deletions app/recover_funds_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//go:build !experimental
// +build !experimental

package app

import (
"encoding/json"
"os"
"testing"
"time"

"github.com/CosmWasm/wasmd/app"
"github.com/cosmos/cosmos-sdk/simapp"
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"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"
)

func TestLostFunds(t *testing.T) {
// address with funds inaccessible
lostAddr, err := sdk.AccAddressFromBech32("regen1c3lpjaq0ytdtsrnjqzmtj3hceavl8fe2vtkj7f")
require.NoError(t, err)

// address that the community member has access to
newAddr, err := sdk.AccAddressFromBech32("regen14tpuqrwf95evu3ejm9z7dn20ttcyzqy3jjpfv4")
require.NoError(t, err)

vestingPeriods := vestingtypes.Periods{
{
Length: 0,
Amount: sdk.NewCoins(sdk.NewInt64Coin("uregen", 1000000)),
},
{
Length: 28630800,
Amount: sdk.NewCoins(sdk.NewInt64Coin("uregen", 406041682)),
},
{
Length: 2629746,
Amount: sdk.NewCoins(sdk.NewInt64Coin("uregen", 406041666)),
},
{
Length: 2629746,
Amount: sdk.NewCoins(sdk.NewInt64Coin("uregen", 406041666)),
},
}

encCfg := MakeEncodingConfig()
db := dbm.NewMemDB()

regenApp := NewRegenApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{},
app.DefaultNodeHome, 0, encCfg, simapp.EmptyAppOptions{}, nil)
bz := NewDefaultGenesisState(encCfg.Marshaler)
stateBytes, err := json.MarshalIndent(bz, "", " ")
require.NoError(t, err)

regenApp.InitChain(
abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: app.DefaultConsensusParams,
AppStateBytes: stateBytes,
},
)

ctx := regenApp.BaseApp.NewContext(false, tmproto.Header{})

regenApp.AccountKeeper.SetAccount(ctx, authtypes.NewBaseAccount(newAddr, nil, 9068, 4))
oldAccountBalance := sdk.NewCoins(sdk.NewCoin("uregen", sdk.NewInt(1219125014)))
regenApp.AccountKeeper.SetAccount(ctx, vestingtypes.NewPeriodicVestingAccount(authtypes.NewBaseAccount(lostAddr, nil, 146, 0),
oldAccountBalance, 1618498800, vestingPeriods))

regenApp.BankKeeper.MintCoins(ctx, minttypes.ModuleName, oldAccountBalance)
regenApp.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, lostAddr, oldAccountBalance)

newAccountBalance := sdk.NewCoins(sdk.NewInt64Coin("uregen", 10747843))
regenApp.BankKeeper.MintCoins(ctx, minttypes.ModuleName, newAccountBalance)
regenApp.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, newAddr, newAccountBalance)

ctx = ctx.WithBlockTime(time.Now())
err = recoverFunds(ctx, regenApp.AccountKeeper, regenApp.BankKeeper)
require.NoError(t, err)

require.True(t, regenApp.BankKeeper.GetBalance(ctx, lostAddr, "uregen").IsZero())
newbalance := regenApp.BankKeeper.GetAllBalances(ctx, newAddr)
// verify new account pre-existing balance also included
require.Equal(t, newbalance, newAccountBalance.Add(oldAccountBalance...))

acc := regenApp.AccountKeeper.GetAccount(ctx, newAddr)
pva, ok := acc.(*vestingtypes.PeriodicVestingAccount)
require.True(t, ok)
require.Equal(t, pva.GetVestingPeriods().String(), vestingPeriods.String())
}
91 changes: 90 additions & 1 deletion app/stable_appconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
package app

import (
"fmt"

"github.com/CosmWasm/wasmd/x/wasm"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client"
"github.com/cosmos/cosmos-sdk/x/gov"
paramsclient "github.com/cosmos/cosmos-sdk/x/params/client"
Expand All @@ -20,12 +23,18 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"

"github.com/regen-network/regen-ledger/types/module/server"
"github.com/regen-network/regen-ledger/x/ecocredit"
ecocreditcore "github.com/regen-network/regen-ledger/x/ecocredit/client/core"
"github.com/regen-network/regen-ledger/x/ecocredit/client/marketplace"
ecocreditmodule "github.com/regen-network/regen-ledger/x/ecocredit/module"
)

func setCustomModuleBasics() []module.AppModuleBasic {
Expand Down Expand Up @@ -59,7 +68,87 @@ func setCustomOrderEndBlocker() []string {
return []string{}
}

func (app *RegenApp) registerUpgradeHandlers() {}
func (app *RegenApp) registerUpgradeHandlers() {

// mainnet upgrade handler
const upgradeName = "v4.0.0"
app.UpgradeKeeper.SetUpgradeHandler(upgradeName, func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
// run state migrations for sdk modules
toVersion, err := app.mm.RunMigrations(ctx, app.configurator, fromVM)
if err != nil {
return nil, err
}

// run x/ecocredit state migrations
if err := app.smm.RunMigrations(ctx, app.AppCodec()); err != nil {
return nil, err
}
toVersion[ecocredit.ModuleName] = ecocreditmodule.Module{}.ConsensusVersion()

// recover funds for community member (regen-1 governance proposal #11)
if ctx.ChainID() == "regen-1" {
if err := recoverFunds(ctx, app.AccountKeeper, app.BankKeeper); err != nil {
return nil, err
}
}

return toVersion, nil
})

}

func recoverFunds(ctx sdk.Context, ak authkeeper.AccountKeeper, bk bankkeeper.Keeper) error {
// address with funds inaccessible
lostAddr, err := sdk.AccAddressFromBech32("regen1c3lpjaq0ytdtsrnjqzmtj3hceavl8fe2vtkj7f")
if err != nil {
return err
}

// address that the community member has access to
newAddr, err := sdk.AccAddressFromBech32("regen14tpuqrwf95evu3ejm9z7dn20ttcyzqy3jjpfv4")
if err != nil {
return err
}

lostAccount := ak.GetAccount(ctx, lostAddr)
if lostAccount == nil {
return fmt.Errorf("%s account not found", lostAccount.GetAddress().String())
}

newAccount := ak.GetAccount(ctx, newAddr)
if newAccount == nil {
return fmt.Errorf("%s account not found", newAccount.GetAddress().String())
}

va, ok := lostAccount.(*vestingtypes.PeriodicVestingAccount)
if !ok {
return fmt.Errorf("%s is not a vesting account", lostAddr)
}

vestingPeriods := va.VestingPeriods
// unlock vesting tokens
newVestingPeriods := make([]vestingtypes.Period, len(va.VestingPeriods))
for i, vp := range va.VestingPeriods {
vp.Length = 0
newVestingPeriods[i] = vp
}
va.VestingPeriods = newVestingPeriods
ak.SetAccount(ctx, va)

// send spendable balance from lost account to new account
spendable := bk.SpendableCoins(ctx, lostAccount.GetAddress())
if err := bk.SendCoins(ctx, lostAccount.GetAddress(), newAccount.GetAddress(), spendable); err != nil {
return err
}

newPVA := vestingtypes.NewPeriodicVestingAccount(
authtypes.NewBaseAccount(newAccount.GetAddress(), newAccount.GetPubKey(), newAccount.GetAccountNumber(), newAccount.GetSequence()),
va.OriginalVesting, va.StartTime, vestingPeriods,
)
ak.SetAccount(ctx, newPVA)

return nil
}

func (app *RegenApp) setCustomAnteHandler(encCfg simappparams.EncodingConfig, wasmKey *sdk.KVStoreKey, _ *wasm.Config) (sdk.AnteHandler, error) {
return ante.NewAnteHandler(
Expand Down
17 changes: 12 additions & 5 deletions x/ecocredit/migrations/v3/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"google.golang.org/protobuf/types/known/timestamppb"

basketapi "github.com/regen-network/regen-ledger/api/regen/ecocredit/basket/v1"
api "github.com/regen-network/regen-ledger/api/regen/ecocredit/v1"
"github.com/regen-network/regen-ledger/orm"
"github.com/regen-network/regen-ledger/x/ecocredit/core"
Expand All @@ -25,7 +26,7 @@ type batchMapT struct {

// MigrateState performs in-place store migrations from v3.0 to v4.0.
func MigrateState(sdkCtx sdk.Context, storeKey storetypes.StoreKey,
cdc codec.Codec, ss api.StateStore, subspace paramtypes.Subspace) error {
cdc codec.Codec, ss api.StateStore, basketStore basketapi.StateStore, subspace paramtypes.Subspace) error {
classInfoTableBuilder, err := orm.NewPrimaryKeyTableBuilder(ClassInfoTablePrefix, storeKey, &ClassInfo{}, cdc)
if err != nil {
return err
Expand Down Expand Up @@ -153,9 +154,10 @@ func MigrateState(sdkCtx sdk.Context, storeKey storetypes.StoreKey,
}

// migrate credit batches to ORM v1 and create projects for existing credit classes
batchDenomToBatchMap := make(map[string]batchMapT) // map of a batch denom to batch id and amount cancelled
projectKeyToBatchSeq := make(map[uint64]uint64) // map of a project key to batch sequence
classKeyToProjectSeq := make(map[uint64]uint64) // map of a class key to project sequence
batchDenomToBatchMap := make(map[string]batchMapT) // map of a batch denom to batch id and amount cancelled
projectKeyToBatchSeq := make(map[uint64]uint64) // map of a project key to batch sequence
classKeyToProjectSeq := make(map[uint64]uint64) // map of a class key to project sequence
oldBatchDenomToNewDenomMap := make(map[string]string) // map of a old batch denom to new batch denom
batchItr, err := batchInfoTable.PrefixScan(sdkCtx, nil, nil)
if err != nil {
return err
Expand Down Expand Up @@ -226,7 +228,7 @@ func MigrateState(sdkCtx sdk.Context, storeKey storetypes.StoreKey,
projectId = id
}

startDate, endDate, err := parseBatchDenom(batchInfo.BatchDenom)
startDate, endDate, err := ParseBatchDenom(batchInfo.BatchDenom)
if err != nil {
return err
}
Expand All @@ -241,6 +243,7 @@ func MigrateState(sdkCtx sdk.Context, storeKey storetypes.StoreKey,
return err
}

oldBatchDenomToNewDenomMap[batchInfo.BatchDenom] = batchDenom
batchIssuer, err := sdk.AccAddressFromBech32(batchInfo.Issuer)
if err != nil {
return err
Expand Down Expand Up @@ -321,6 +324,10 @@ func MigrateState(sdkCtx sdk.Context, storeKey storetypes.StoreKey,
return err
}

if err := patchMigrate(ctx, sdkCtx, ss, basketStore, oldBatchDenomToNewDenomMap); err != nil {
return err
}

return nil
}

Expand Down
7 changes: 6 additions & 1 deletion x/ecocredit/migrations/v3/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

regenorm "github.com/regen-network/regen-ledger/orm"

basketapi "github.com/regen-network/regen-ledger/api/regen/ecocredit/basket/v1"
api "github.com/regen-network/regen-ledger/api/regen/ecocredit/v1"
"github.com/regen-network/regen-ledger/x/ecocredit"
"github.com/regen-network/regen-ledger/x/ecocredit/core"
Expand All @@ -46,6 +47,7 @@ func TestMigrations(t *testing.T) {
assert.NilError(t, cms.LoadLatestVersion())
ormCtx := ormtable.WrapContextDefault(ormtest.NewMemoryBackend())
sdkCtx := sdk.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()).WithContext(ormCtx)

store := sdkCtx.KVStore(ecocreditKey)

paramStore.WithKeyTable(v3.ParamKeyTable())
Expand Down Expand Up @@ -171,7 +173,10 @@ func TestMigrations(t *testing.T) {
ss, err := api.NewStateStore(ormdb)
require.Nil(t, err)

err = v3.MigrateState(sdkCtx, ecocreditKey, encCfg.Marshaler, ss, paramStore)
basketStore, err := basketapi.NewStateStore(ormdb)
require.Nil(t, err)

err = v3.MigrateState(sdkCtx, ecocreditKey, encCfg.Marshaler, ss, basketStore, paramStore)
require.NoError(t, err)

ctx := sdk.WrapSDKContext(sdkCtx)
Expand Down
2 changes: 1 addition & 1 deletion x/ecocredit/migrations/v3/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func IterateSupplies(store sdk.KVStore, storeKey byte, cb func(denom, supply str
return nil
}

func parseBatchDenom(denom string) (*time.Time, *time.Time, error) {
func ParseBatchDenom(denom string) (*time.Time, *time.Time, error) {
// batch denom format: <class id>-<start date>-<end date>-<batch seq no>
result := strings.Split(denom, "-")
if len(result) != 4 {
Expand Down
2 changes: 1 addition & 1 deletion x/ecocredit/migrations/v3/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestParseBatchDenom(t *testing.T) {

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
sd, ed, err := parseBatchDenom(tc.denom)
sd, ed, err := ParseBatchDenom(tc.denom)
if tc.expErr {
require.Error(t, err)
require.Contains(t, err.Error(), tc.errMessage)
Expand Down
Loading

0 comments on commit 1abcee8

Please sign in to comment.