Skip to content

Commit 350901e

Browse files
feat: in-place testnet creator (backport #7374) (#7424)
* feat: in-place testnet creator (#7374) * plug in testnetify * fix import * utilize appOpts * add upgrade trigger * update sdk fork tag * add changelog (cherry picked from commit 0b32654) # Conflicts: # CHANGELOG.md # go.sum * fix conflicts * go mod tidy --------- Co-authored-by: Adam Tucker <[email protected]> Co-authored-by: Adam Tucker <[email protected]>
1 parent 9693eb7 commit 350901e

File tree

9 files changed

+299
-18
lines changed

9 files changed

+299
-18
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5050

5151
* [#7395](https://github.com/osmosis-labs/osmosis/pull/7395) Adds logging to track incentive accumulator truncation.
5252

53+
### Misc Improvements
54+
55+
* [#7374](https://github.com/osmosis-labs/osmosis/pull/7374) In place testnet creation CLI.
56+
5357
## v22.0.1
5458

5559
### Bug Fixes

app/app.go

+231
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"path/filepath"
99
"reflect"
10+
"time"
1011

1112
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
1213

@@ -41,12 +42,15 @@ import (
4142

4243
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
4344
reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1"
45+
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
4446

4547
runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services"
4648

4749
"github.com/CosmWasm/wasmd/x/wasm"
4850
dbm "github.com/cometbft/cometbft-db"
4951
abci "github.com/cometbft/cometbft/abci/types"
52+
"github.com/cometbft/cometbft/crypto"
53+
"github.com/cometbft/cometbft/libs/bytes"
5054
tmjson "github.com/cometbft/cometbft/libs/json"
5155
"github.com/cometbft/cometbft/libs/log"
5256
tmos "github.com/cometbft/cometbft/libs/os"
@@ -66,12 +70,14 @@ import (
6670
"github.com/cosmos/cosmos-sdk/server/config"
6771
servertypes "github.com/cosmos/cosmos-sdk/server/types"
6872
sdk "github.com/cosmos/cosmos-sdk/types"
73+
"github.com/cosmos/cosmos-sdk/types/bech32"
6974
"github.com/cosmos/cosmos-sdk/types/module"
7075
"github.com/cosmos/cosmos-sdk/version"
7176
"github.com/cosmos/cosmos-sdk/x/auth/ante"
7277
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
7378
"github.com/cosmos/cosmos-sdk/x/crisis"
7479

80+
minttypes "github.com/osmosis-labs/osmosis/v22/x/mint/types"
7581
protorevtypes "github.com/osmosis-labs/osmosis/v22/x/protorev/types"
7682

7783
"github.com/osmosis-labs/osmosis/v22/app/keepers"
@@ -403,6 +409,231 @@ func NewOsmosisApp(
403409
return app
404410
}
405411

412+
// InitOsmosisAppForTestnet is broken down into two sections:
413+
// Required Changes: Changes that, if not made, will cause the testnet to halt or panic
414+
// Optional Changes: Changes to customize the testnet to one's liking (lower vote times, fund accounts, etc)
415+
func InitOsmosisAppForTestnet(app *OsmosisApp, newValAddr bytes.HexBytes, newValPubKey crypto.PubKey, newOperatorAddress, upgradeToTrigger string) *OsmosisApp {
416+
//
417+
// Required Changes:
418+
//
419+
420+
ctx := app.BaseApp.NewUncachedContext(true, tmproto.Header{})
421+
pubkey := &ed25519.PubKey{Key: newValPubKey.Bytes()}
422+
pubkeyAny, err := types.NewAnyWithValue(pubkey)
423+
if err != nil {
424+
tmos.Exit(err.Error())
425+
}
426+
427+
// STAKING
428+
//
429+
430+
// Create Validator struct for our new validator.
431+
_, bz, err := bech32.DecodeAndConvert(newOperatorAddress)
432+
if err != nil {
433+
tmos.Exit(err.Error())
434+
}
435+
bech32Addr, err := bech32.ConvertAndEncode("osmovaloper", bz)
436+
if err != nil {
437+
tmos.Exit(err.Error())
438+
}
439+
newVal := stakingtypes.Validator{
440+
OperatorAddress: bech32Addr,
441+
ConsensusPubkey: pubkeyAny,
442+
Jailed: false,
443+
Status: stakingtypes.Bonded,
444+
Tokens: sdk.NewInt(900000000000000),
445+
DelegatorShares: sdk.MustNewDecFromStr("10000000"),
446+
Description: stakingtypes.Description{
447+
Moniker: "Testnet Validator",
448+
},
449+
Commission: stakingtypes.Commission{
450+
CommissionRates: stakingtypes.CommissionRates{
451+
Rate: sdk.MustNewDecFromStr("0.05"),
452+
MaxRate: sdk.MustNewDecFromStr("0.1"),
453+
MaxChangeRate: sdk.MustNewDecFromStr("0.05"),
454+
},
455+
},
456+
MinSelfDelegation: sdk.OneInt(),
457+
}
458+
459+
// Remove all validators from power store
460+
stakingKey := app.GetKey(stakingtypes.ModuleName)
461+
stakingStore := ctx.KVStore(stakingKey)
462+
iterator := app.StakingKeeper.ValidatorsPowerStoreIterator(ctx)
463+
for ; iterator.Valid(); iterator.Next() {
464+
stakingStore.Delete(iterator.Key())
465+
}
466+
iterator.Close()
467+
468+
// Remove all valdiators from last validators store
469+
iterator = app.StakingKeeper.LastValidatorsIterator(ctx)
470+
for ; iterator.Valid(); iterator.Next() {
471+
stakingStore.Delete(iterator.Key())
472+
}
473+
iterator.Close()
474+
475+
// Add our validator to power and last validators store
476+
app.StakingKeeper.SetValidator(ctx, newVal)
477+
err = app.StakingKeeper.SetValidatorByConsAddr(ctx, newVal)
478+
if err != nil {
479+
tmos.Exit(err.Error())
480+
}
481+
app.StakingKeeper.SetValidatorByPowerIndex(ctx, newVal)
482+
app.StakingKeeper.SetLastValidatorPower(ctx, newVal.GetOperator(), 0)
483+
if err := app.StakingKeeper.Hooks().AfterValidatorCreated(ctx, newVal.GetOperator()); err != nil {
484+
panic(err)
485+
}
486+
487+
// DISTRIBUTION
488+
//
489+
490+
// Initialize records for this validator across all distribution stores
491+
app.DistrKeeper.SetValidatorHistoricalRewards(ctx, newVal.GetOperator(), 0, distrtypes.NewValidatorHistoricalRewards(sdk.DecCoins{}, 1))
492+
app.DistrKeeper.SetValidatorCurrentRewards(ctx, newVal.GetOperator(), distrtypes.NewValidatorCurrentRewards(sdk.DecCoins{}, 1))
493+
app.DistrKeeper.SetValidatorAccumulatedCommission(ctx, newVal.GetOperator(), distrtypes.InitialValidatorAccumulatedCommission())
494+
app.DistrKeeper.SetValidatorOutstandingRewards(ctx, newVal.GetOperator(), distrtypes.ValidatorOutstandingRewards{Rewards: sdk.DecCoins{}})
495+
496+
// SLASHING
497+
//
498+
499+
// Set validator signing info for our new validator.
500+
newConsAddr := sdk.ConsAddress(newValAddr.Bytes())
501+
newValidatorSigningInfo := slashingtypes.ValidatorSigningInfo{
502+
Address: newConsAddr.String(),
503+
StartHeight: app.LastBlockHeight() - 1,
504+
Tombstoned: false,
505+
}
506+
app.SlashingKeeper.SetValidatorSigningInfo(ctx, newConsAddr, newValidatorSigningInfo)
507+
508+
//
509+
// Optional Changes:
510+
//
511+
512+
// GOV
513+
//
514+
515+
newExpeditedVotingPeriod := time.Minute
516+
newVotingPeriod := time.Minute * 2
517+
518+
govParams := app.GovKeeper.GetParams(ctx)
519+
govParams.ExpeditedVotingPeriod = &newExpeditedVotingPeriod
520+
govParams.VotingPeriod = &newVotingPeriod
521+
govParams.MinDeposit = sdk.NewCoins(sdk.NewInt64Coin("uosmo", 100000000))
522+
govParams.ExpeditedMinDeposit = sdk.NewCoins(sdk.NewInt64Coin("uosmo", 150000000))
523+
524+
err = app.GovKeeper.SetParams(ctx, govParams)
525+
if err != nil {
526+
tmos.Exit(err.Error())
527+
}
528+
529+
// EPOCHS
530+
//
531+
532+
dayEpochInfo := app.EpochsKeeper.GetEpochInfo(ctx, "day")
533+
dayEpochInfo.Duration = time.Hour * 6
534+
// Prevents epochs from running back to back
535+
dayEpochInfo.CurrentEpochStartTime = time.Now().UTC()
536+
dayEpochInfo.CurrentEpochStartHeight = app.LastBlockHeight()
537+
app.EpochsKeeper.DeleteEpochInfo(ctx, "day")
538+
err = app.EpochsKeeper.AddEpochInfo(ctx, dayEpochInfo)
539+
if err != nil {
540+
tmos.Exit(err.Error())
541+
}
542+
543+
weekEpochInfo := app.EpochsKeeper.GetEpochInfo(ctx, "week")
544+
weekEpochInfo.Duration = time.Hour * 12
545+
// Prevents epochs from running back to back
546+
weekEpochInfo.CurrentEpochStartTime = time.Now().UTC()
547+
weekEpochInfo.CurrentEpochStartHeight = app.LastBlockHeight()
548+
app.EpochsKeeper.DeleteEpochInfo(ctx, "week")
549+
err = app.EpochsKeeper.AddEpochInfo(ctx, weekEpochInfo)
550+
if err != nil {
551+
tmos.Exit(err.Error())
552+
}
553+
554+
// BANK
555+
//
556+
557+
defaultCoins := sdk.NewCoins(
558+
sdk.NewInt64Coin("ibc/0CD3A0285E1341859B5E86B6AB7682F023D03E97607CCC1DC95706411D866DF7", 1000000000000), // DAI
559+
sdk.NewInt64Coin("uosmo", 1000000000000),
560+
sdk.NewInt64Coin("uion", 1000000000))
561+
562+
localOsmosisAccounts := []sdk.AccAddress{
563+
sdk.MustAccAddressFromBech32("osmo12smx2wdlyttvyzvzg54y2vnqwq2qjateuf7thj"),
564+
sdk.MustAccAddressFromBech32("osmo1cyyzpxplxdzkeea7kwsydadg87357qnahakaks"),
565+
sdk.MustAccAddressFromBech32("osmo18s5lynnmx37hq4wlrw9gdn68sg2uxp5rgk26vv"),
566+
sdk.MustAccAddressFromBech32("osmo1qwexv7c6sm95lwhzn9027vyu2ccneaqad4w8ka"),
567+
sdk.MustAccAddressFromBech32("osmo14hcxlnwlqtq75ttaxf674vk6mafspg8xwgnn53"),
568+
sdk.MustAccAddressFromBech32("osmo12rr534cer5c0vj53eq4y32lcwguyy7nndt0u2t"),
569+
sdk.MustAccAddressFromBech32("osmo1nt33cjd5auzh36syym6azgc8tve0jlvklnq7jq"),
570+
sdk.MustAccAddressFromBech32("osmo10qfrpash5g2vk3hppvu45x0g860czur8ff5yx0"),
571+
sdk.MustAccAddressFromBech32("osmo1f4tvsdukfwh6s9swrc24gkuz23tp8pd3e9r5fa"),
572+
sdk.MustAccAddressFromBech32("osmo1myv43sqgnj5sm4zl98ftl45af9cfzk7nhjxjqh"),
573+
sdk.MustAccAddressFromBech32("osmo14gs9zqh8m49yy9kscjqu9h72exyf295afg6kgk"),
574+
sdk.MustAccAddressFromBech32("osmo1jllfytsz4dryxhz5tl7u73v29exsf80vz52ucc")}
575+
576+
// Fund localosmosis accounts
577+
for _, account := range localOsmosisAccounts {
578+
err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, defaultCoins)
579+
if err != nil {
580+
tmos.Exit(err.Error())
581+
}
582+
err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, account, defaultCoins)
583+
if err != nil {
584+
tmos.Exit(err.Error())
585+
}
586+
}
587+
588+
// Fund edgenet faucet
589+
faucetCoins := sdk.NewCoins(
590+
sdk.NewInt64Coin("ibc/0CD3A0285E1341859B5E86B6AB7682F023D03E97607CCC1DC95706411D866DF7", 1000000000000000), // DAI
591+
sdk.NewInt64Coin("uosmo", 1000000000000000),
592+
sdk.NewInt64Coin("uion", 1000000000000))
593+
err = app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, faucetCoins)
594+
if err != nil {
595+
tmos.Exit(err.Error())
596+
}
597+
err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, sdk.MustAccAddressFromBech32("osmo1rqgf207csps822qwmd3k2n6k6k4e99w502e79t"), faucetCoins)
598+
if err != nil {
599+
tmos.Exit(err.Error())
600+
}
601+
602+
// Mars bank account
603+
marsCoins := sdk.NewCoins(
604+
sdk.NewInt64Coin("uosmo", 10000000000000),
605+
sdk.NewInt64Coin("ibc/903A61A498756EA560B85A85132D3AEE21B5DEDD41213725D22ABF276EA6945E", 400000000000),
606+
sdk.NewInt64Coin("ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858", 3000000000000),
607+
sdk.NewInt64Coin("ibc/C140AFD542AE77BD7DCC83F13FDD8C5E5BB8C4929785E6EC2F4C636F98F17901", 200000000000),
608+
sdk.NewInt64Coin("ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", 700000000000),
609+
sdk.NewInt64Coin("ibc/D1542AA8762DB13087D8364F3EA6509FD6F009A34F00426AF9E4F9FA85CBBF1F", 2000000000),
610+
sdk.NewInt64Coin("ibc/EA1D43981D5C9A1C4AAEA9C23BB1D4FA126BA9BC7020A25E0AE4AA841EA25DC5", 3000000000000000000))
611+
err = app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, marsCoins)
612+
if err != nil {
613+
tmos.Exit(err.Error())
614+
}
615+
err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, sdk.MustAccAddressFromBech32("osmo1ev02crc36675xd8s029qh7wg3wjtfk37jr004z"), marsCoins)
616+
if err != nil {
617+
tmos.Exit(err.Error())
618+
}
619+
620+
// UPGRADE
621+
//
622+
623+
if upgradeToTrigger != "" {
624+
upgradePlan := upgradetypes.Plan{
625+
Name: upgradeToTrigger,
626+
Height: app.LastBlockHeight(),
627+
}
628+
err = app.UpgradeKeeper.ScheduleUpgrade(ctx, upgradePlan)
629+
if err != nil {
630+
panic(err)
631+
}
632+
}
633+
634+
return app
635+
}
636+
406637
// MakeCodecs returns the application codec and a legacy Amino codec.
407638
func MakeCodecs() (codec.Codec, *codec.LegacyAmino) {
408639
config := MakeEncodingConfig()

cmd/osmosisd/cmd/root.go

+52-6
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ import (
2222

2323
"github.com/osmosis-labs/osmosis/osmomath"
2424
"github.com/osmosis-labs/osmosis/v22/app/params"
25+
v22 "github.com/osmosis-labs/osmosis/v22/app/upgrades/v22" // TODO: should be automated to be updated to current version every upgrade
2526
"github.com/osmosis-labs/osmosis/v22/ingest/sqs"
2627

2728
tmcfg "github.com/cometbft/cometbft/config"
29+
"github.com/cometbft/cometbft/crypto"
30+
"github.com/cometbft/cometbft/libs/bytes"
2831
tmcli "github.com/cometbft/cometbft/libs/cli"
2932
"github.com/cometbft/cometbft/libs/log"
3033
tmtypes "github.com/cometbft/cometbft/types"
@@ -55,6 +58,7 @@ import (
5558
genutil "github.com/cosmos/cosmos-sdk/x/genutil"
5659
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
5760
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
61+
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
5862

5963
"github.com/CosmWasm/wasmd/x/wasm"
6064
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
@@ -682,6 +686,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
682686
)
683687

684688
server.AddCommands(rootCmd, osmosis.DefaultNodeHome, newApp, createOsmosisAppAndExport, addModuleInitFlags)
689+
server.AddTestnetCreatorCommand(rootCmd, osmosis.DefaultNodeHome, newTestnetApp, addModuleInitFlags)
685690

686691
for i, cmd := range rootCmd.Commands() {
687692
if cmd.Name() == "start" {
@@ -828,12 +833,7 @@ func newApp(logger log.Logger, db cometbftdb.DB, traceStore io.Writer, appOpts s
828833
wasmOpts = append(wasmOpts, wasmkeeper.WithVMCacheMetrics(prometheus.DefaultRegisterer))
829834
}
830835

831-
return osmosis.NewOsmosisApp(
832-
logger, db, traceStore, true, skipUpgradeHeights,
833-
cast.ToString(appOpts.Get(flags.FlagHome)),
834-
cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)),
835-
appOpts,
836-
wasmOpts,
836+
baseAppOptions := []func(*baseapp.BaseApp){
837837
baseapp.SetPruning(pruningOpts),
838838
baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))),
839839
baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))),
@@ -847,9 +847,55 @@ func newApp(logger log.Logger, db cometbftdb.DB, traceStore io.Writer, appOpts s
847847
baseapp.SetIAVLCacheSize(cast.ToInt(appOpts.Get(server.FlagIAVLCacheSize))),
848848
baseapp.SetIAVLDisableFastNode(cast.ToBool(appOpts.Get(server.FlagDisableIAVLFastNode))),
849849
baseapp.SetChainID(chainID),
850+
}
851+
852+
// If this is an in place testnet, set any new stores that may exist
853+
if cast.ToBool(appOpts.Get(server.KeyIsTestnet)) {
854+
version := store.NewCommitMultiStore(db).LatestVersion() + 1
855+
baseAppOptions = append(baseAppOptions, baseapp.SetStoreLoader(upgradetypes.UpgradeStoreLoader(version, &v22.Upgrade.StoreUpgrades)))
856+
}
857+
858+
return osmosis.NewOsmosisApp(
859+
logger, db, traceStore, true, skipUpgradeHeights,
860+
cast.ToString(appOpts.Get(flags.FlagHome)),
861+
cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)),
862+
appOpts,
863+
wasmOpts,
864+
baseAppOptions...,
850865
)
851866
}
852867

868+
// newTestnetApp starts by running the normal newApp method. From there, the app interface returned is modified in order
869+
// for a testnet to be created from the provided app.
870+
func newTestnetApp(logger log.Logger, db cometbftdb.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application {
871+
// Create an app and type cast to an OsmosisApp
872+
app := newApp(logger, db, traceStore, appOpts)
873+
osmosisApp, ok := app.(*osmosis.OsmosisApp)
874+
if !ok {
875+
panic("app created from newApp is not of type osmosisApp")
876+
}
877+
878+
newValAddr, ok := appOpts.Get(server.KeyNewValAddr).(bytes.HexBytes)
879+
if !ok {
880+
panic("newValAddr is not of type bytes.HexBytes")
881+
}
882+
newValPubKey, ok := appOpts.Get(server.KeyUserPubKey).(crypto.PubKey)
883+
if !ok {
884+
panic("newValPubKey is not of type crypto.PubKey")
885+
}
886+
newOperatorAddress, ok := appOpts.Get(server.KeyNewOpAddr).(string)
887+
if !ok {
888+
panic("newOperatorAddress is not of type string")
889+
}
890+
upgradeToTrigger, ok := appOpts.Get(server.KeyTriggerTestnetUpgrade).(string)
891+
if !ok {
892+
panic("upgradeToTrigger is not of type string")
893+
}
894+
895+
// Make modifications to the normal OsmosisApp required to run the network locally
896+
return osmosis.InitOsmosisAppForTestnet(osmosisApp, newValAddr, newValPubKey, newOperatorAddress, upgradeToTrigger)
897+
}
898+
853899
// createOsmosisAppAndExport creates and exports the new Osmosis app, returns the state of the new Osmosis app for a genesis file.
854900
func createOsmosisAppAndExport(
855901
logger log.Logger, db cometbftdb.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string,

go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -384,9 +384,9 @@ replace (
384384
// v1.0.0-beta.3 is incompatible, so we use v1.0.0-beta.2
385385
github.com/cosmos/cosmos-proto => github.com/cosmos/cosmos-proto v1.0.0-beta.2
386386

387-
// Our cosmos-sdk branch is: https://github.com/osmosis-labs/cosmos-sdk/tree/osmo-v22/v0.47.5, current branch: osmo-v22/v0.47.5. Direct commit link: https://github.com/osmosis-labs/cosmos-sdk/commit/cf358f6fc20cabfb5cb8ce75fc10b2623150869e
387+
// Our cosmos-sdk branch is: https://github.com/osmosis-labs/cosmos-sdk/tree/osmo-v22/v0.47.5, current branch: osmo-v22/v0.47.5. Direct commit link: https://github.com/osmosis-labs/cosmos-sdk/commit/97b06aa49e4c814f9ec4e64be2d5eaf7be42a259
388388
// https://github.com/osmosis-labs/cosmos-sdk/releases/tag/v0.47.5-v22-osmo-2
389-
github.com/cosmos/cosmos-sdk => github.com/osmosis-labs/cosmos-sdk v0.47.5-v22-osmo-2
389+
github.com/cosmos/cosmos-sdk => github.com/osmosis-labs/cosmos-sdk v0.47.5-v22-osmo-3
390390
github.com/cosmos/gogoproto => github.com/cosmos/gogoproto v1.4.10
391391
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
392392

0 commit comments

Comments
 (0)