Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 245 additions & 0 deletions cmd/atomoned/cmd/inplace_testnet.go
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

View workflow job for this annotation

GitHub Actions / Analyze

"fmt" imported and not used

Check failure on line 5 in cmd/atomoned/cmd/inplace_testnet.go

View workflow job for this annotation

GitHub Actions / golangci-lint

"fmt" imported and not used

Check failure on line 5 in cmd/atomoned/cmd/inplace_testnet.go

View workflow job for this annotation

GitHub Actions / golangci-lint

"fmt" imported and not used

Check failure on line 5 in cmd/atomoned/cmd/inplace_testnet.go

View workflow job for this annotation

GitHub Actions / liveness-test

"fmt" imported and not used

Check failure on line 5 in cmd/atomoned/cmd/inplace_testnet.go

View workflow job for this annotation

GitHub Actions / tests

"fmt" imported and not used
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused import

"io"
"os"

Check failure on line 7 in cmd/atomoned/cmd/inplace_testnet.go

View workflow job for this annotation

GitHub Actions / Analyze

"os" imported and not used

Check failure on line 7 in cmd/atomoned/cmd/inplace_testnet.go

View workflow job for this annotation

GitHub Actions / golangci-lint

"os" imported and not used) (typecheck)

Check failure on line 7 in cmd/atomoned/cmd/inplace_testnet.go

View workflow job for this annotation

GitHub Actions / golangci-lint

"os" imported and not used (typecheck)

Check failure on line 7 in cmd/atomoned/cmd/inplace_testnet.go

View workflow job for this annotation

GitHub Actions / liveness-test

"os" imported and not used

Check failure on line 7 in cmd/atomoned/cmd/inplace_testnet.go

View workflow job for this annotation

GitHub Actions / tests

"os" imported and not used
"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))

// 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)
}
}
1 change: 1 addition & 0 deletions cmd/atomoned/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func initRootCmd(
) {
rootCmd.AddCommand(
genutilcli.InitCmd(basicManager, atomone.DefaultNodeHome),
NewInPlaceTestnetCmd(),
tmcli.NewCompletionCmd(rootCmd, true),
NewTestnetCmd(basicManager, banktypes.GenesisBalancesIterator{}),
addDebugCommands(debug.Cmd()),
Expand Down
Loading