-
Notifications
You must be signed in to change notification settings - Fork 37
feat: wire in place testnet command #268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
42ac595
5530269
b399d83
4c6324b
bec1539
8c237c5
0ec099d
dc51792
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,245 @@ | ||
| package cmd | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
|
Check failure on line 5 in cmd/atomoned/cmd/inplace_testnet.go
|
||
| "io" | ||
| "os" | ||
|
Check failure on line 7 in cmd/atomoned/cmd/inplace_testnet.go
|
||
| "strings" | ||
|
|
||
| "github.com/spf13/cast" | ||
| "github.com/spf13/cobra" | ||
|
|
||
| "github.com/cometbft/cometbft/crypto" | ||
| "github.com/cometbft/cometbft/libs/bytes" | ||
| cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" | ||
|
|
||
| dbm "github.com/cosmos/cosmos-db" | ||
|
|
||
| "cosmossdk.io/log" | ||
| "cosmossdk.io/math" | ||
| storetypes "cosmossdk.io/store/types" | ||
|
|
||
| "github.com/cosmos/cosmos-sdk/client/flags" | ||
| codectypes "github.com/cosmos/cosmos-sdk/codec/types" | ||
| "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" | ||
| "github.com/cosmos/cosmos-sdk/server" | ||
| servertypes "github.com/cosmos/cosmos-sdk/server/types" | ||
| sdk "github.com/cosmos/cosmos-sdk/types" | ||
| distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" | ||
| minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" | ||
| slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" | ||
| stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" | ||
|
|
||
| atomone "github.com/atomone-hub/atomone/app" | ||
| "github.com/atomone-hub/atomone/app/params" | ||
| ) | ||
|
|
||
| const valVotingPower int64 = 900000000000000 | ||
|
|
||
| var flagAccountsToFund = "accounts-to-fund" | ||
|
|
||
| type valArgs struct { | ||
| newValAddr bytes.HexBytes | ||
| newOperatorAddress string | ||
| newValPubKey crypto.PubKey | ||
| accountsToFund []string | ||
| upgradeToTrigger string | ||
| homeDir string | ||
| } | ||
|
|
||
| func NewInPlaceTestnetCmd() *cobra.Command { | ||
| cmd := server.InPlaceTestnetCreator(newTestnetApp) | ||
| cmd.Example = `atomoned in-place-testnet testing-1 atonevaloper1w7f3xx7e75p4l7qdym5msqem9rd4dyc4jfa7ag --home $HOME/.atomone/validator1 --validator-privkey=6dq+/KHNvyiw2TToCgOpUpQKIzrLs69Rb8Az39xvmxPHNoPxY1Cil8FY+4DhT9YwD6s0tFABMlLcpaylzKKBOg== --accounts-to-fund="atone1f7twgcq4ypzg7y24wuywy06xmdet8pc4m7dv9c,atone1qvuhm5m644660nd8377d6l7yz9e9hhm9hv8p87"` | ||
|
|
||
| cmd.Flags().String(flagAccountsToFund, "", "Comma-separated list of account addresses that will be funded for testing purposes") | ||
| return cmd | ||
| } | ||
|
|
||
| // newTestnetApp starts by running the normal newApp method. From there, the app interface returned is modified in order | ||
| // for a testnet to be created from the provided app. | ||
| func newTestnetApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application { | ||
| // Create an app and type cast to an App | ||
| newApp := newApp(logger, db, traceStore, appOpts) | ||
| testApp, ok := newApp.(*atomone.AtomOneApp) | ||
| if !ok { | ||
| panic("app created from newApp is not of type App") | ||
| } | ||
|
|
||
| // Get command args | ||
| args, err := getCommandArgs(appOpts) | ||
| if err != nil { | ||
| panic(err) | ||
| } | ||
|
|
||
| return initAppForTestnet(testApp, args) | ||
| } | ||
|
|
||
| func initAppForTestnet(app *atomone.AtomOneApp, args valArgs) *atomone.AtomOneApp { | ||
| // Required Changes: | ||
| // | ||
| ctx := app.NewUncachedContext(true, cmtproto.Header{}) | ||
|
|
||
| pubkey := &ed25519.PubKey{Key: args.newValPubKey.Bytes()} | ||
| pubkeyAny, err := codectypes.NewAnyWithValue(pubkey) | ||
| handleErr(err) | ||
|
|
||
| // STAKING | ||
| // | ||
|
|
||
| // Create Validator struct for our new validator. | ||
| newVal := stakingtypes.Validator{ | ||
| OperatorAddress: args.newOperatorAddress, | ||
| ConsensusPubkey: pubkeyAny, | ||
| Jailed: false, | ||
| Status: stakingtypes.Bonded, | ||
| Tokens: math.NewInt(valVotingPower), | ||
| DelegatorShares: math.LegacyMustNewDecFromStr("10000000"), | ||
| Description: stakingtypes.Description{ | ||
| Moniker: "Testnet Validator", | ||
| }, | ||
| Commission: stakingtypes.Commission{ | ||
| CommissionRates: stakingtypes.CommissionRates{ | ||
| Rate: math.LegacyMustNewDecFromStr("0.05"), | ||
| MaxRate: math.LegacyMustNewDecFromStr("0.1"), | ||
| MaxChangeRate: math.LegacyMustNewDecFromStr("0.05"), | ||
| }, | ||
| }, | ||
| MinSelfDelegation: math.OneInt(), | ||
| } | ||
|
|
||
| validator, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(newVal.GetOperator()) | ||
| handleErr(err) | ||
|
|
||
| // Remove all validators from power store | ||
| stakingKey := app.GetKey(stakingtypes.ModuleName) | ||
| stakingStore := ctx.KVStore(stakingKey) | ||
| iterator, err := app.StakingKeeper.ValidatorsPowerStoreIterator(ctx) | ||
| handleErr(err) | ||
|
|
||
| for ; iterator.Valid(); iterator.Next() { | ||
| stakingStore.Delete(iterator.Key()) | ||
| } | ||
| iterator.Close() | ||
|
|
||
| // Remove all validators from last validators store | ||
| iterator, err = app.StakingKeeper.LastValidatorsIterator(ctx) | ||
| handleErr(err) | ||
|
|
||
| for ; iterator.Valid(); iterator.Next() { | ||
| stakingStore.Delete(iterator.Key()) | ||
| } | ||
| iterator.Close() | ||
|
|
||
| // Remove all validators from validators store | ||
| iterator = stakingStore.Iterator(stakingtypes.ValidatorsKey, storetypes.PrefixEndBytes(stakingtypes.ValidatorsKey)) | ||
| for ; iterator.Valid(); iterator.Next() { | ||
| stakingStore.Delete(iterator.Key()) | ||
| } | ||
| iterator.Close() | ||
|
|
||
| // Remove all validators from unbonding queue | ||
| iterator = stakingStore.Iterator(stakingtypes.ValidatorQueueKey, storetypes.PrefixEndBytes(stakingtypes.ValidatorQueueKey)) | ||
| for ; iterator.Valid(); iterator.Next() { | ||
| stakingStore.Delete(iterator.Key()) | ||
| } | ||
| iterator.Close() | ||
|
|
||
| // Add our validator to power and last validators store | ||
| handleErr(app.StakingKeeper.SetValidator(ctx, newVal)) | ||
| handleErr(app.StakingKeeper.SetValidatorByConsAddr(ctx, newVal)) | ||
| handleErr(app.StakingKeeper.SetValidatorByPowerIndex(ctx, newVal)) | ||
| handleErr(app.StakingKeeper.SetLastValidatorPower(ctx, validator, 0)) | ||
| handleErr(app.StakingKeeper.Hooks().AfterValidatorCreated(ctx, validator)) | ||
Pantani marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // DISTRIBUTION | ||
| // | ||
|
|
||
| // Initialize records for this validator across all distribution stores | ||
| handleErr(app.DistrKeeper.SetValidatorHistoricalRewards(ctx, validator, 0, distrtypes.NewValidatorHistoricalRewards(sdk.DecCoins{}, 1))) | ||
| handleErr(app.DistrKeeper.SetValidatorCurrentRewards(ctx, validator, distrtypes.NewValidatorCurrentRewards(sdk.DecCoins{}, 1))) | ||
| handleErr(app.DistrKeeper.SetValidatorAccumulatedCommission(ctx, validator, distrtypes.InitialValidatorAccumulatedCommission())) | ||
| handleErr(app.DistrKeeper.SetValidatorOutstandingRewards(ctx, validator, distrtypes.ValidatorOutstandingRewards{Rewards: sdk.DecCoins{}})) | ||
|
|
||
| // SLASHING | ||
| // | ||
|
|
||
| // Set validator signing info for our new validator. | ||
| newConsAddr := sdk.ConsAddress(args.newValAddr.Bytes()) | ||
| newValidatorSigningInfo := slashingtypes.ValidatorSigningInfo{ | ||
| Address: newConsAddr.String(), | ||
| StartHeight: app.LastBlockHeight() - 1, | ||
| Tombstoned: false, | ||
| } | ||
| _ = app.SlashingKeeper.SetValidatorSigningInfo(ctx, newConsAddr, newValidatorSigningInfo) | ||
|
|
||
| // BANK | ||
| // | ||
| bondDenom, err := app.StakingKeeper.BondDenom(ctx) | ||
| handleErr(err) | ||
| if bondDenom == "" { | ||
| bondDenom = params.BondDenom | ||
| } | ||
|
|
||
| defaultCoins := sdk.NewCoins(sdk.NewInt64Coin(bondDenom, 1000000000)) | ||
tbruyelle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // Fund local accounts | ||
| for _, accountStr := range args.accountsToFund { | ||
| handleErr(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, defaultCoins)) | ||
|
|
||
| account, err := app.AccountKeeper.AddressCodec().StringToBytes(accountStr) | ||
| handleErr(err) | ||
|
|
||
| handleErr(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, account, defaultCoins)) | ||
| } | ||
|
|
||
| return app | ||
| } | ||
|
|
||
| // parse the input flags and returns valArgs | ||
| func getCommandArgs(appOpts servertypes.AppOptions) (valArgs, error) { | ||
| args := valArgs{} | ||
|
|
||
| newValAddr, ok := appOpts.Get(server.KeyNewValAddr).(bytes.HexBytes) | ||
| if !ok { | ||
| return args, errors.New("newValAddr is not of type bytes.HexBytes") | ||
| } | ||
| args.newValAddr = newValAddr | ||
| newValPubKey, ok := appOpts.Get(server.KeyUserPubKey).(crypto.PubKey) | ||
| if !ok { | ||
| return args, errors.New("newValPubKey is not of type crypto.PubKey") | ||
| } | ||
| args.newValPubKey = newValPubKey | ||
| newOperatorAddress, ok := appOpts.Get(server.KeyNewOpAddr).(string) | ||
| if !ok { | ||
| return args, errors.New("newOperatorAddress is not of type string") | ||
| } | ||
| args.newOperatorAddress = newOperatorAddress | ||
| upgradeToTrigger, ok := appOpts.Get(server.KeyTriggerTestnetUpgrade).(string) | ||
| if !ok { | ||
| return args, errors.New("upgradeToTrigger is not of type string") | ||
| } | ||
| args.upgradeToTrigger = upgradeToTrigger | ||
|
|
||
| // parsing and set accounts to fund | ||
| accountsString := cast.ToString(appOpts.Get(flagAccountsToFund)) | ||
| if len(accountsString) > 0 { | ||
| args.accountsToFund = strings.Split(accountsString, ",") | ||
| } | ||
|
|
||
| // home dir | ||
| homeDir := cast.ToString(appOpts.Get(flags.FlagHome)) | ||
| if homeDir == "" { | ||
| return args, errors.New("invalid home dir") | ||
| } | ||
| args.homeDir = homeDir | ||
|
|
||
| return args, nil | ||
| } | ||
|
|
||
| // handleErr prints the error and exits the program if the error is not nil | ||
| func handleErr(err error) { | ||
| if err != nil { | ||
| panic(err) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused import