From 91570fb018c20dbb074abcfb671fc8b49d9a929b Mon Sep 17 00:00:00 2001 From: Randy Grok Date: Tue, 13 Aug 2024 13:24:39 +0200 Subject: [PATCH] temp commit, include logic for zeroheight --- runtime/v2/builder.go | 2 +- server/v2/types.go | 26 ++++ simapp/v2/app_di.go | 1 - simapp/v2/export.go | 213 +++++++++++++++++++++++++++++++ simapp/v2/simdv2/cmd/commands.go | 10 +- 5 files changed, 245 insertions(+), 7 deletions(-) diff --git a/runtime/v2/builder.go b/runtime/v2/builder.go index f428d53d0a29..39031bbc830d 100644 --- a/runtime/v2/builder.go +++ b/runtime/v2/builder.go @@ -69,7 +69,7 @@ func (a *AppBuilder[T]) RegisterModules(modules map[string]appmodulev2.AppModule // RegisterStores registers the provided store keys. // This method should only be used for registering extra stores -// wiich is necessary for modules that not registered using the app config. +// which is necessary for modules that not registered using the app config. // To be used in combination of RegisterModules. func (a *AppBuilder[T]) RegisterStores(keys ...string) { a.app.storeKeys = append(a.app.storeKeys, keys...) diff --git a/server/v2/types.go b/server/v2/types.go index 579855984fb9..36ade2a8c40d 100644 --- a/server/v2/types.go +++ b/server/v2/types.go @@ -2,8 +2,10 @@ package serverv2 import ( "encoding/json" + dbm "github.com/cosmos/cosmos-db" gogoproto "github.com/cosmos/gogoproto/proto" "github.com/spf13/viper" + "io" coreapp "cosmossdk.io/core/app" "cosmossdk.io/core/transaction" @@ -30,3 +32,27 @@ type ExportedApp struct { // Height is the app's latest block height. Height int64 } + +// AppExporter is a function that dumps all app state to +// JSON-serializable structure and returns the current validator set. +type AppExporter func( + logger log.Logger, + db dbm.DB, + traceWriter io.Writer, + height int64, + forZeroHeight bool, + jailAllowedAddrs []string, + opts AppOptions, + modulesToExport []string, +) (ExportedApp, error) + +// AppOptions defines an interface that is passed into an application +// constructor, typically used to set BaseApp options that are either supplied +// via config file or through CLI arguments/flags. The underlying implementation +// is defined by the server package and is typically implemented via a Viper +// literal defined on the server Context. Note, casting Get calls may not yield +// the expected types and could result in type assertion errors. It is recommend +// to either use the cast package or perform manual conversion for safety. +type AppOptions interface { + Get(string) interface{} +} diff --git a/simapp/v2/app_di.go b/simapp/v2/app_di.go index c0bbb7f85cfb..f8ade01aa55b 100644 --- a/simapp/v2/app_di.go +++ b/simapp/v2/app_di.go @@ -2,7 +2,6 @@ package simapp import ( _ "embed" - "github.com/spf13/viper" clienthelpers "cosmossdk.io/client/v2/helpers" diff --git a/simapp/v2/export.go b/simapp/v2/export.go index c1d66be3c9cf..d752f17c06c4 100644 --- a/simapp/v2/export.go +++ b/simapp/v2/export.go @@ -2,6 +2,12 @@ package simapp import ( "context" + "cosmossdk.io/collections" + slashingtypes "cosmossdk.io/x/slashing/types" + stakingtypes "cosmossdk.io/x/staking/types" + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "log" serverv2 "cosmossdk.io/server/v2" ) @@ -27,3 +33,210 @@ func (app *SimApp[T]) ExportAppStateAndValidators(forZeroHeight bool, jailAllowe Height: int64(latestHeight), }, nil } + +// prepare for fresh start at zero height +// NOTE zero height genesis is a temporary feature which will be deprecated +// +// in favor of export at a block height +func (app *SimApp[T]) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { + //applyAllowedAddrs := false + // + //// check if there is a allowed address list + //if len(jailAllowedAddrs) > 0 { + // applyAllowedAddrs = true + //} + + allowedAddrsMap := make(map[string]bool) + + for _, addr := range jailAllowedAddrs { + _, err := sdk.ValAddressFromBech32(addr) + if err != nil { + log.Fatal(err) + } + allowedAddrsMap[addr] = true + } + + /* Handle fee distribution state. */ + + // withdraw all validator commission + err := app.StakingKeeper.IterateValidators(ctx, func(_ int64, val sdk.ValidatorI) (stop bool) { + valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) + if err != nil { + panic(err) + } + _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, valBz) + return false + }) + if err != nil { + panic(err) + } + + // withdraw all delegator rewards + dels, err := app.StakingKeeper.GetAllDelegations(ctx) + if err != nil { + panic(err) + } + + for _, delegation := range dels { + valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) + if err != nil { + panic(err) + } + + delAddr := sdk.MustAccAddressFromBech32(delegation.DelegatorAddress) + + _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr) + } + + // clear validator slash events + err = app.DistrKeeper.ValidatorSlashEvents.Clear(ctx, nil) + if err != nil { + panic(err) + } + + // clear validator historical rewards + err = app.DistrKeeper.ValidatorHistoricalRewards.Clear(ctx, nil) + if err != nil { + panic(err) + } + + // set context height to zero + height := ctx.BlockHeight() + ctx = ctx.WithBlockHeight(0) + + // reinitialize all validators + err = app.StakingKeeper.IterateValidators(ctx, func(_ int64, val sdk.ValidatorI) (stop bool) { + valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) + if err != nil { + panic(err) + } + // donate any unwithdrawn outstanding reward tokens to the community pool + rewards, err := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, valBz) + if err != nil { + panic(err) + } + feePool, err := app.DistrKeeper.FeePool.Get(ctx) + if err != nil { + panic(err) + } + feePool.DecimalPool = feePool.DecimalPool.Add(rewards...) // distribution will allocate this to the protocolpool eventually + if err := app.DistrKeeper.FeePool.Set(ctx, feePool); err != nil { + panic(err) + } + + if err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, valBz); err != nil { + panic(err) + } + return false + }) + if err != nil { + panic(err) + } + + // reinitialize all delegations + for _, del := range dels { + valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress) + if err != nil { + panic(err) + } + delAddr := sdk.MustAccAddressFromBech32(del.DelegatorAddress) + + if err := app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil { + // never called as BeforeDelegationCreated always returns nil + panic(fmt.Errorf("error while incrementing period: %w", err)) + } + + if err := app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr); err != nil { + // never called as AfterDelegationModified always returns nil + panic(fmt.Errorf("error while creating a new delegation period record: %w", err)) + } + } + + // reset context height + ctx = ctx.WithBlockHeight(height) + + /* Handle staking state. */ + + // iterate through redelegations, reset creation height + err = app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { + for i := range red.Entries { + red.Entries[i].CreationHeight = 0 + } + err = app.StakingKeeper.SetRedelegation(ctx, red) + if err != nil { + panic(err) + } + return false + }) + if err != nil { + panic(err) + } + + // iterate through unbonding delegations, reset creation height + err = app.StakingKeeper.UnbondingDelegations.Walk( + ctx, + nil, + func(key collections.Pair[[]byte, []byte], ubd stakingtypes.UnbondingDelegation) (stop bool, err error) { + for i := range ubd.Entries { + ubd.Entries[i].CreationHeight = 0 + } + err = app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + if err != nil { + return true, err + } + return false, err + }, + ) + if err != nil { + panic(err) + } + + //// Iterate through validators by power descending, reset bond heights, and + //// update bond intra-tx counters. + //store := ctx.KVStore(app.GetKey(stakingtypes.StoreKey)) + //iter := storetypes.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) + //counter := int16(0) + + //for ; iter.Valid(); iter.Next() { + // addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) + // validator, err := app.StakingKeeper.GetValidator(ctx, addr) + // if err != nil { + // panic("expected validator, not found") + // } + // + // validator.UnbondingHeight = 0 + // if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { + // validator.Jailed = true + // } + // + // if err = app.StakingKeeper.SetValidator(ctx, validator); err != nil { + // panic(err) + // } + // counter++ + //} + // + //if err := iter.Close(); err != nil { + // app.Logger().Error("error while closing the key-value store reverse prefix iterator: ", err) + // return + //} + + _, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + if err != nil { + log.Fatal(err) + } + + /* Handle slashing state. */ + + // reset start height on signing infos + err = app.SlashingKeeper.ValidatorSigningInfo.Walk(ctx, nil, func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool, err error) { + info.StartHeight = 0 + err = app.SlashingKeeper.ValidatorSigningInfo.Set(ctx, addr, info) + if err != nil { + return true, err + } + return false, nil + }) + if err != nil { + panic(err) + } +} diff --git a/simapp/v2/simdv2/cmd/commands.go b/simapp/v2/simdv2/cmd/commands.go index 8d1e8bd40a63..e74c0d4bbe39 100644 --- a/simapp/v2/simdv2/cmd/commands.go +++ b/simapp/v2/simdv2/cmd/commands.go @@ -92,13 +92,13 @@ func genesisCommand[T transaction.Tx]( jailAllowedAddrs []string, viper *viper.Viper, modulesToExport []string, - ) (servertypes.ExportedApp, error), + ) (serverv2.ExportedApp, error), cmds ...*cobra.Command, ) *cobra.Command { - compatAppExporter := func(logger log.Logger, db dbm.DB, traceWriter io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOpts servertypes.AppOptions, modulesToExport []string) (servertypes.ExportedApp, error) { + compatAppExporter := func(logger log.Logger, db dbm.DB, traceWriter io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOpts servertypes.AppOptions, modulesToExport []string) (serverv2.ExportedApp, error) { viperAppOpts, ok := appOpts.(*viper.Viper) if !ok { - return servertypes.ExportedApp{}, errors.New("appOpts is not viper.Viper") + return serverv2.ExportedApp{}, errors.New("appOpts is not viper.Viper") } return appExport(logger, height, forZeroHeight, jailAllowedAddrs, viperAppOpts, modulesToExport) @@ -165,7 +165,7 @@ func appExport[T transaction.Tx]( jailAllowedAddrs []string, viper *viper.Viper, modulesToExport []string, -) (servertypes.ExportedApp, error) { +) (serverv2.ExportedApp, error) { // overwrite the FlagInvCheckPeriod viper.Set(server.FlagInvCheckPeriod, 1) @@ -174,7 +174,7 @@ func appExport[T transaction.Tx]( simApp = simapp.NewSimApp[T](logger, viper) if err := simApp.LoadHeight(uint64(height)); err != nil { - return servertypes.ExportedApp{}, err + return serverv2.ExportedApp{}, err } } else { simApp = simapp.NewSimApp[T](logger, viper)