Skip to content
Merged
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
257 changes: 257 additions & 0 deletions app/testnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package app

import (
"fmt"
"strings"
"time"

"github.com/cosmos/cosmos-sdk/codec/types"
storetypes "github.com/cosmos/cosmos-sdk/store/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"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
tmos "github.com/tendermint/tendermint/libs/os"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

utypes "github.com/akash-network/node/upgrades/types"
)

type TestnetValidator struct {
OperatorAddress sdk.ValAddress
ConsensusAddress sdk.ConsAddress
ConsensusPubKey *types.Any
Moniker string
Commission stakingtypes.Commission
MinSelfDelegation sdk.Int
}

type TestnetUpgrade struct {
Name string
}

type TestnetVotingPeriod struct {
time.Duration
}

type TestnetGovConfig struct {
VotingParams *struct {
VotingPeriod TestnetVotingPeriod `json:"voting_period,omitempty"`
} `json:"voting_params,omitempty"`
}

type TestnetConfig struct {
Accounts []sdk.AccAddress
Validators []TestnetValidator
Gov TestnetGovConfig
Upgrade TestnetUpgrade
}

func TrimQuotes(data string) string {
data = strings.TrimPrefix(data, "\"")
return strings.TrimSuffix(data, "\"")
}

func (t *TestnetVotingPeriod) UnmarshalJSON(data []byte) error {
val := TrimQuotes(string(data))

if !strings.HasSuffix(val, "s") {
return fmt.Errorf("invalid format of voting period. must contain time unit. Valid time units are ns|us(µs)|ms|s|m|h") // nolint: goerr113
}
Comment on lines +60 to +62
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove overly restrictive time unit validation.

The validation only allows durations ending with 's', but Go's time.ParseDuration supports multiple units (ns, us, µs, ms, s, m, h). The error message mentions these units but the code rejects them.

-	if !strings.HasSuffix(val, "s") {
-		return fmt.Errorf("invalid format of voting period. must contain time unit. Valid time units are ns|us(µs)|ms|s|m|h") // nolint: goerr113
-	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if !strings.HasSuffix(val, "s") {
return fmt.Errorf("invalid format of voting period. must contain time unit. Valid time units are ns|us(µs)|ms|s|m|h") // nolint: goerr113
}
🤖 Prompt for AI Agents
In app/testnet.go around lines 60 to 62, the code restricts the voting period
duration to only those ending with 's', which conflicts with the error message
and Go's time.ParseDuration support for multiple units. Remove the check that
enforces the suffix 's' so that all valid time units supported by
time.ParseDuration (ns, us, µs, ms, s, m, h) are accepted, and keep the error
message as is to reflect the valid units.


var err error
t.Duration, err = time.ParseDuration(val)
if err != nil {
return err
}

return nil
}

// InitAkashAppForTestnet is broken down into two sections:
// Required Changes: Changes that, if not made, will cause the testnet to halt or panic
// Optional Changes: Changes to customize the testnet to one's liking (lower vote times, fund accounts, etc)
func InitAkashAppForTestnet(
app *AkashApp,
tcfg *TestnetConfig,
) *AkashApp {
//
// Required Changes:
//

var err error

defer func() {
if err != nil {
tmos.Exit(err.Error())
}
}()
Comment on lines +86 to +90
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid using os.Exit in library functions.

Using tmos.Exit in a defer block makes this function unsuitable for use in tests or as a library function. It prevents proper error handling by the caller.

-	defer func() {
-		if err != nil {
-			tmos.Exit(err.Error())
-		}
-	}()

Instead, return the error and let the caller decide how to handle it.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/testnet.go around lines 86 to 90, the defer function calls tmos.Exit on
error, which abruptly terminates the program and prevents callers from handling
errors properly. Remove the defer block that calls tmos.Exit and instead modify
the function to return the error to the caller, allowing them to decide how to
handle it.


ctx := app.BaseApp.NewUncachedContext(true, tmproto.Header{})

// Remove all validators from power store
stakingKey := app.GetKey(stakingtypes.ModuleName)
stakingStore := ctx.KVStore(stakingKey)
iterator := app.Keepers.Cosmos.Staking.ValidatorsPowerStoreIterator(ctx)

for ; iterator.Valid(); iterator.Next() {
stakingStore.Delete(iterator.Key())
}
if err := iterator.Close(); err != nil {
panic(err)
}

// Remove all validators from last validators store
iterator = app.Keepers.Cosmos.Staking.LastValidatorsIterator(ctx)

for ; iterator.Valid(); iterator.Next() {
stakingStore.Delete(iterator.Key())
}
if err := iterator.Close(); err != nil {
panic(err)
}

// Remove all validators from validator store
iterator = storetypes.KVStorePrefixIterator(stakingStore, stakingtypes.ValidatorsKey)
for ; iterator.Valid(); iterator.Next() {
stakingStore.Delete(iterator.Key())
}
if err := iterator.Close(); err != nil {
panic(err)
}

// Remove all validators from unbonding queue
iterator = storetypes.KVStorePrefixIterator(stakingStore, stakingtypes.ValidatorQueueKey)
for ; iterator.Valid(); iterator.Next() {
stakingStore.Delete(iterator.Key())
}
if err := iterator.Close(); err != nil {
panic(err)
}

for _, val := range tcfg.Validators {
// Create Validator struct for our new validator.
newVal := stakingtypes.Validator{
OperatorAddress: val.OperatorAddress.String(),
ConsensusPubkey: val.ConsensusPubKey,
Jailed: false,
Status: stakingtypes.Bonded,
Tokens: sdk.NewInt(900000000000000),
DelegatorShares: sdk.MustNewDecFromStr("10000000"),
Description: stakingtypes.Description{
Moniker: "Testnet Validator",
},
Commission: stakingtypes.Commission{
CommissionRates: stakingtypes.CommissionRates{
Rate: sdk.MustNewDecFromStr("0.05"),
MaxRate: sdk.MustNewDecFromStr("0.1"),
MaxChangeRate: sdk.MustNewDecFromStr("0.05"),
},
},
MinSelfDelegation: sdk.OneInt(),
}

// Add our validator to power and last validators store
app.Keepers.Cosmos.Staking.SetValidator(ctx, newVal)
err = app.Keepers.Cosmos.Staking.SetValidatorByConsAddr(ctx, newVal)
if err != nil {
return nil
}

app.Keepers.Cosmos.Staking.SetValidatorByPowerIndex(ctx, newVal)

valAddr := newVal.GetOperator()
app.Keepers.Cosmos.Staking.SetLastValidatorPower(ctx, valAddr, 0)

app.Keepers.Cosmos.Distr.Hooks().AfterValidatorCreated(ctx, valAddr)
app.Keepers.Cosmos.Slashing.Hooks().AfterValidatorCreated(ctx, valAddr)

// DISTRIBUTION
//

// Initialize records for this validator across all distribution stores
app.Keepers.Cosmos.Distr.SetValidatorHistoricalRewards(ctx, valAddr, 0, distrtypes.NewValidatorHistoricalRewards(sdk.DecCoins{}, 1))
app.Keepers.Cosmos.Distr.SetValidatorCurrentRewards(ctx, valAddr, distrtypes.NewValidatorCurrentRewards(sdk.DecCoins{}, 1))
app.Keepers.Cosmos.Distr.SetValidatorAccumulatedCommission(ctx, valAddr, distrtypes.InitialValidatorAccumulatedCommission())
app.Keepers.Cosmos.Distr.SetValidatorOutstandingRewards(ctx, valAddr, distrtypes.ValidatorOutstandingRewards{Rewards: sdk.DecCoins{}})

// SLASHING
//

newConsAddr := val.ConsensusAddress

// Set validator signing info for our new validator.
newValidatorSigningInfo := slashingtypes.ValidatorSigningInfo{
Address: newConsAddr.String(),
StartHeight: app.LastBlockHeight() - 1,
Tombstoned: false,
}

_, _ = app.Keepers.Cosmos.Staking.ApplyAndReturnValidatorSetUpdates(ctx)

app.Keepers.Cosmos.Slashing.SetValidatorSigningInfo(ctx, newConsAddr, newValidatorSigningInfo)
}

//
// Optional Changes:
//

// GOV
//

voteParams := app.Keepers.Cosmos.Gov.GetVotingParams(ctx)
voteParams.VotingPeriod = tcfg.Gov.VotingParams.VotingPeriod.Duration
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add nil check for VotingParams.

Accessing tcfg.Gov.VotingParams.VotingPeriod without checking if VotingParams is nil could cause a panic.

+	if tcfg.Gov.VotingParams != nil {
 	voteParams.VotingPeriod = tcfg.Gov.VotingParams.VotingPeriod.Duration
 	app.Keepers.Cosmos.Gov.SetVotingParams(ctx, voteParams)
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
voteParams.VotingPeriod = tcfg.Gov.VotingParams.VotingPeriod.Duration
if tcfg.Gov.VotingParams != nil {
voteParams.VotingPeriod = tcfg.Gov.VotingParams.VotingPeriod.Duration
app.Keepers.Cosmos.Gov.SetVotingParams(ctx, voteParams)
}
🤖 Prompt for AI Agents
In app/testnet.go at line 197, add a nil check for tcfg.Gov.VotingParams before
accessing VotingPeriod to prevent a potential panic. Ensure you verify that
VotingParams is not nil, and only then assign VotingPeriod from its Duration
field.

app.Keepers.Cosmos.Gov.SetVotingParams(ctx, voteParams)

// BANK
//

defaultCoins := sdk.NewCoins(
sdk.NewInt64Coin("uakt", 1000000000000),
sdk.NewInt64Coin("ibc/12C6A0C374171B595A0A9E18B83FA09D295FB1F2D8C6DAA3AC28683471752D84", 1000000000000), // axlUSDC
)

for _, account := range tcfg.Accounts {
err := app.Keepers.Cosmos.Bank.MintCoins(ctx, minttypes.ModuleName, defaultCoins)
if err != nil {
return nil
}
err = app.Keepers.Cosmos.Bank.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, account, defaultCoins)
if err != nil {
return nil
}
}

// UPGRADE
//
if tcfg.Upgrade.Name != "" {
upgradePlan := upgradetypes.Plan{
Name: tcfg.Upgrade.Name,
Height: app.LastBlockHeight() + 10,
}

err = app.Keepers.Cosmos.Upgrade.ScheduleUpgrade(ctx, upgradePlan)
if err != nil {
panic(err)
}

for name, fn := range utypes.GetUpgradesList() {
upgrade, err := fn(app.Logger(), &app.App)
if err != nil {
panic(err)
}

if tcfg.Upgrade.Name == name {
app.Logger().Info(fmt.Sprintf("configuring upgrade `%s`", name))
if storeUpgrades := upgrade.StoreLoader(); storeUpgrades != nil && tcfg.Upgrade.Name == name {
app.Logger().Info(fmt.Sprintf("setting up store upgrades for `%s`", name))
app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(app.LastBlockHeight(), storeUpgrades))
}
}
}
}

return app
}
31 changes: 31 additions & 0 deletions cmd/akash/cmd/app_creator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cmd

import (
"io"

"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"

servertypes "github.com/cosmos/cosmos-sdk/server/types"

akash "github.com/akash-network/node/app"
"github.com/akash-network/node/cmd/akash/cmd/testnetify"
)

// 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 AkashApp
app := newApp(logger, db, traceStore, appOpts)
akashApp, ok := app.(*akash.AkashApp)
if !ok {
panic("app created from newApp is not of type AkashApp")
}

tcfg, valid := appOpts.Get(testnetify.KeyTestnetConfig).(*akash.TestnetConfig)
if !valid {
panic("cflags.KeyTestnetConfig is not of type akash.TestnetConfig")
}

// Make modifications to the normal AkashApp required to run the network locally
return akash.InitAkashAppForTestnet(akashApp, tcfg)
}
2 changes: 1 addition & 1 deletion cmd/akash/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ func ExecuteWithCtx(ctx context.Context, rootCmd *cobra.Command, envPrefix strin
func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
debugCmd := debug.Cmd()
debugCmd.AddCommand(ConvertBech32Cmd())
debugCmd.AddCommand(testnetify.Cmd())

rootCmd.AddCommand(
rpc.StatusCommand(),
Expand All @@ -158,6 +157,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
sdkserver.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler),
)

rootCmd.AddCommand(testnetify.GetCmd(newTestnetApp))
rootCmd.AddCommand(server.Commands(app.DefaultHome, newApp, createAppAndExport, addModuleInitFlags)...)

rootCmd.SetOut(rootCmd.OutOrStdout())
Expand Down
31 changes: 0 additions & 31 deletions cmd/akash/cmd/testnetify/accounts.go

This file was deleted.

Loading
Loading