Skip to content

Commit d6d2957

Browse files
almk-devvladjdkaljo242
authored
feat(testnet): add configurable testnet validator powers (#708)
* add configurable validator power * add changelog * update flag string * update comments * remove string parsing from validation function and update tests --------- Co-authored-by: Vlad J <[email protected]> Co-authored-by: Alex | Cosmos Labs <[email protected]>
1 parent bf5b130 commit d6d2957

File tree

4 files changed

+204
-49
lines changed

4 files changed

+204
-49
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
### IMPROVEMENTS
2727

28+
- [\#708](https://github.com/cosmos/evm/pull/708) Add configurable testnet validator powers
2829
- [\#698](https://github.com/cosmos/evm/pull/698) Expose mempool configuration flags and move mempool configuration in app.go to helper
2930
- [\#538](https://github.com/cosmos/evm/pull/538) Optimize `eth_estimateGas` gRPC path: short-circuit plain transfers, add optimistic gas bound based on `MaxUsedGas`.
3031
- [\#513](https://github.com/cosmos/evm/pull/513) Replace `TestEncodingConfig` with production `EncodingConfig` in encoding package to remove test dependencies from production code.

evmd/cmd/evmd/cmd/testnet.go

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bufio"
55
"encoding/json"
66
"fmt"
7-
87
"net"
98
"os"
109
"path/filepath"
@@ -32,7 +31,7 @@ import (
3231
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
3332
servertypes "github.com/cosmos/cosmos-sdk/server/types"
3433
"github.com/cosmos/cosmos-sdk/testutil"
35-
"github.com/cosmos/cosmos-sdk/testutil/network"
34+
sdknetwork "github.com/cosmos/cosmos-sdk/testutil/network"
3635
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
3736
sdk "github.com/cosmos/cosmos-sdk/types"
3837
"github.com/cosmos/cosmos-sdk/types/module"
@@ -47,6 +46,7 @@ import (
4746
cosmosevmhd "github.com/cosmos/evm/crypto/hd"
4847
cosmosevmkeyring "github.com/cosmos/evm/crypto/keyring"
4948
"github.com/cosmos/evm/evmd"
49+
customnetwork "github.com/cosmos/evm/evmd/tests/network"
5050
cosmosevmserverconfig "github.com/cosmos/evm/server/config"
5151
evmnetwork "github.com/cosmos/evm/testutil/integration/evm/network"
5252
evmtypes "github.com/cosmos/evm/x/vm/types"
@@ -66,6 +66,7 @@ var (
6666
flagPrintMnemonic = "print-mnemonic"
6767
flagSingleHost = "single-host"
6868
flagCommitTimeout = "commit-timeout"
69+
flagValidatorPowers = "validator-powers"
6970
unsafeStartValidatorFn UnsafeStartValidatorCmdCreator
7071
)
7172

@@ -92,20 +93,22 @@ type initArgs struct {
9293
startingIPAddress string
9394
singleMachine bool
9495
useDocker bool
96+
validatorPowers []int64
9597
}
9698

9799
type startArgs struct {
98-
algo string
99-
apiAddress string
100-
chainID string
101-
enableLogging bool
102-
grpcAddress string
103-
minGasPrices string
104-
numValidators int
105-
outputDir string
106-
printMnemonic bool
107-
rpcAddress string
108-
timeoutCommit time.Duration
100+
algo string
101+
apiAddress string
102+
chainID string
103+
enableLogging bool
104+
grpcAddress string
105+
minGasPrices string
106+
numValidators int
107+
outputDir string
108+
printMnemonic bool
109+
rpcAddress string
110+
timeoutCommit time.Duration
111+
validatorPowers []int64
109112
}
110113

111114
func addTestnetFlagsToCmd(cmd *cobra.Command) {
@@ -114,6 +117,7 @@ func addTestnetFlagsToCmd(cmd *cobra.Command) {
114117
cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
115118
cmd.Flags().String(server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)")
116119
cmd.Flags().String(flags.FlagKeyType, string(cosmosevmhd.EthSecp256k1Type), "Key signing algorithm to generate keys for")
120+
cmd.Flags().IntSlice(flagValidatorPowers, []int{}, "Comma-separated list of validator powers (e.g. '100,200,150'). If not specified, all validators have equal power of 100. Last value is repeated for remaining validators.")
117121

118122
// support old flags name for backwards compatibility
119123
cmd.Flags().SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
@@ -193,6 +197,12 @@ Example:
193197
}
194198
args.algo, _ = cmd.Flags().GetString(flags.FlagKeyType)
195199

200+
validatorPowers, _ := cmd.Flags().GetIntSlice(flagValidatorPowers)
201+
args.validatorPowers, err = parseValidatorPowers(validatorPowers, args.numValidators)
202+
if err != nil {
203+
return err
204+
}
205+
196206
return initTestnetFiles(clientCtx, cmd, config, mbm, genBalIterator, args)
197207
},
198208
}
@@ -234,6 +244,13 @@ Example:
234244
args.grpcAddress, _ = cmd.Flags().GetString(flagGRPCAddress)
235245
args.printMnemonic, _ = cmd.Flags().GetBool(flagPrintMnemonic)
236246

247+
validatorPowers, _ := cmd.Flags().GetIntSlice(flagValidatorPowers)
248+
var err error
249+
args.validatorPowers, err = parseValidatorPowers(validatorPowers, args.numValidators)
250+
if err != nil {
251+
return err
252+
}
253+
237254
return startTestnet(cmd, args)
238255
},
239256
}
@@ -249,6 +266,36 @@ Example:
249266

250267
const nodeDirPerm = 0o755
251268

269+
// parseValidatorPowers processes validator powers from an int slice.
270+
// If the slice is empty, returns a slice of default powers (100) for each validator.
271+
// If fewer powers are specified than numValidators, fills remaining with the last specified power.
272+
func parseValidatorPowers(powers []int, numValidators int) ([]int64, error) {
273+
result := make([]int64, numValidators)
274+
if len(powers) == 0 {
275+
for i := 0; i < numValidators; i++ {
276+
result[i] = 100
277+
}
278+
return result, nil
279+
}
280+
281+
for _, power := range powers {
282+
if power <= 0 {
283+
return nil, fmt.Errorf("validator power must be positive, got %d", power)
284+
}
285+
}
286+
287+
for i := 0; i < numValidators; i++ {
288+
if i < len(powers) {
289+
result[i] = int64(powers[i])
290+
} else {
291+
// use the last specified power for remaining validators
292+
result[i] = int64(powers[len(powers)-1])
293+
}
294+
}
295+
296+
return result, nil
297+
}
298+
252299
// initTestnetFiles initializes testnet files for a testnet to be run in a separate process
253300
func initTestnetFiles(
254301
clientCtx client.Context,
@@ -407,7 +454,7 @@ func initTestnetFiles(
407454
genBalances = append(genBalances, bals...)
408455
genAccounts = append(genAccounts, accs...)
409456
}
410-
valTokens := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction)
457+
valTokens := sdk.TokensFromConsensusPower(args.validatorPowers[i], sdk.DefaultPowerReduction)
411458
createValMsg, err := stakingtypes.NewMsgCreateValidator(
412459
sdk.ValAddress(addr).String(),
413460
valPubKeys[i],
@@ -642,7 +689,7 @@ func writeFile(name string, dir string, contents []byte) error {
642689

643690
// startTestnet starts an in-process testnet
644691
func startTestnet(cmd *cobra.Command, args startArgs) error {
645-
networkConfig := network.DefaultConfig(NewTestNetworkFixture)
692+
networkConfig := customnetwork.DefaultConfig()
646693

647694
// Default networkConfig.ChainID is random, and we should only override it if chainID provided
648695
// is non-empty
@@ -652,12 +699,20 @@ func startTestnet(cmd *cobra.Command, args startArgs) error {
652699
networkConfig.SigningAlgo = args.algo
653700
networkConfig.MinGasPrices = args.minGasPrices
654701
networkConfig.NumValidators = args.numValidators
655-
networkConfig.EnableLogging = args.enableLogging
702+
networkConfig.EnableCMTLogging = args.enableLogging
656703
networkConfig.RPCAddress = args.rpcAddress
657704
networkConfig.APIAddress = args.apiAddress
658705
networkConfig.GRPCAddress = args.grpcAddress
659706
networkConfig.PrintMnemonic = args.printMnemonic
660-
networkLogger := network.NewCLILogger(cmd)
707+
708+
// Convert validator powers to bonded tokens
709+
bondedTokensPerValidator := make([]math.Int, len(args.validatorPowers))
710+
for i, power := range args.validatorPowers {
711+
bondedTokensPerValidator[i] = sdk.TokensFromConsensusPower(power, sdk.DefaultPowerReduction)
712+
}
713+
networkConfig.BondedTokensPerValidator = bondedTokensPerValidator
714+
715+
networkLogger := customnetwork.NewCLILogger(cmd)
661716

662717
baseDir := fmt.Sprintf("%s/%s", args.outputDir, networkConfig.ChainID)
663718
if _, err := os.Stat(baseDir); !os.IsNotExist(err) {
@@ -666,7 +721,7 @@ func startTestnet(cmd *cobra.Command, args startArgs) error {
666721
networkConfig.ChainID, baseDir)
667722
}
668723

669-
testnet, err := network.New(networkLogger, baseDir, networkConfig)
724+
testnet, err := customnetwork.New(networkLogger, baseDir, networkConfig)
670725
if err != nil {
671726
return err
672727
}
@@ -684,7 +739,7 @@ func startTestnet(cmd *cobra.Command, args startArgs) error {
684739
}
685740

686741
// NewTestNetworkFixture returns a new evmd AppConstructor for network simulation tests
687-
func NewTestNetworkFixture() network.TestFixture {
742+
func NewTestNetworkFixture() sdknetwork.TestFixture {
688743
dir, err := os.MkdirTemp("", "evm")
689744
if err != nil {
690745
panic(fmt.Sprintf("failed creating temporary directory: %v", err))
@@ -699,7 +754,7 @@ func NewTestNetworkFixture() network.TestFixture {
699754
simtestutil.EmptyAppOptions{},
700755
)
701756

702-
appCtr := func(val network.ValidatorI) servertypes.Application {
757+
appCtr := func(val sdknetwork.ValidatorI) servertypes.Application {
703758
return evmd.NewExampleApp(
704759
log.NewNopLogger(),
705760
dbm.NewMemDB(),
@@ -709,7 +764,7 @@ func NewTestNetworkFixture() network.TestFixture {
709764
)
710765
}
711766

712-
return network.TestFixture{
767+
return sdknetwork.TestFixture{
713768
AppConstructor: appCtr,
714769
GenesisState: app.DefaultGenesis(),
715770
EncodingConfig: moduletestutil.TestEncodingConfig{

evmd/cmd/evmd/cmd/testnet_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package cmd
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestParseValidatorPowers(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
powers []int
13+
numValidators int
14+
want []int64
15+
wantErr bool
16+
}{
17+
{
18+
name: "empty slice - use defaults",
19+
powers: []int{},
20+
numValidators: 4,
21+
want: []int64{100, 100, 100, 100},
22+
wantErr: false,
23+
},
24+
{
25+
name: "nil slice - use defaults",
26+
powers: nil,
27+
numValidators: 4,
28+
want: []int64{100, 100, 100, 100},
29+
wantErr: false,
30+
},
31+
{
32+
name: "exact number of powers",
33+
powers: []int{100, 200, 150, 300},
34+
numValidators: 4,
35+
want: []int64{100, 200, 150, 300},
36+
wantErr: false,
37+
},
38+
{
39+
name: "fewer powers than validators",
40+
powers: []int{100, 200},
41+
numValidators: 5,
42+
want: []int64{100, 200, 200, 200, 200},
43+
wantErr: false,
44+
},
45+
{
46+
name: "single power for all validators",
47+
powers: []int{500},
48+
numValidators: 3,
49+
want: []int64{500, 500, 500},
50+
wantErr: false,
51+
},
52+
{
53+
name: "more powers than validators",
54+
powers: []int{100, 200, 300, 400},
55+
numValidators: 2,
56+
want: []int64{100, 200},
57+
wantErr: false,
58+
},
59+
{
60+
name: "invalid power - negative",
61+
powers: []int{100, -200, 300},
62+
numValidators: 3,
63+
want: nil,
64+
wantErr: true,
65+
},
66+
{
67+
name: "invalid power - zero",
68+
powers: []int{100, 0, 300},
69+
numValidators: 3,
70+
want: nil,
71+
wantErr: true,
72+
},
73+
}
74+
75+
for _, tt := range tests {
76+
t.Run(tt.name, func(t *testing.T) {
77+
got, err := parseValidatorPowers(tt.powers, tt.numValidators)
78+
if tt.wantErr {
79+
require.Error(t, err)
80+
} else {
81+
require.NoError(t, err)
82+
require.Equal(t, tt.want, got)
83+
}
84+
})
85+
}
86+
}

0 commit comments

Comments
 (0)