-
Notifications
You must be signed in to change notification settings - Fork 36
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
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
42ac595
feat: wire in place testnet command
julienrbrt 5530269
lint
julienrbrt b399d83
simplify wrapper
julienrbrt 4c6324b
bump to latest cosmso-sdk with fix
julienrbrt bec1539
Update cmd/atomoned/cmd/inplace_testnet.go
julienrbrt 8c237c5
add bonddenom fallback
julienrbrt 0ec099d
Merge branch 'main' into julien/wireinplace-testnet
julienrbrt dc51792
Apply suggestions from code review
julienrbrt 6608f7f
fix: unused import
tbruyelle 22b490d
docs: in-place-testnet command setup
tbruyelle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,243 @@ | ||
| package cmd | ||
|
|
||
| import ( | ||
| "errors" | ||
| "io" | ||
| "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)) | ||
|
|
||
| // 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.
|
||
|
|
||
| // 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) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.