From a255c672de9def4ecdd20fc06e3f473e45cf35cc Mon Sep 17 00:00:00 2001 From: charithabandi Date: Tue, 4 Mar 2025 10:36:14 -0600 Subject: [PATCH] testnet setup command to generate non-determinstic private keys and improved runtime sanity checks for the genesis validators setup genesis command to validate the validators pubkey --- app/setup/genesis.go | 27 +++++++++++++++++++-------- app/setup/testnet.go | 43 ++++--------------------------------------- config/config.go | 12 ++++++++++++ 3 files changed, 35 insertions(+), 47 deletions(-) diff --git a/app/setup/genesis.go b/app/setup/genesis.go index 8ab6c1878..3895dfe29 100644 --- a/app/setup/genesis.go +++ b/app/setup/genesis.go @@ -167,23 +167,34 @@ func mergeGenesisFlags(conf *config.GenesisConfig, cmd *cobra.Command, flagCfg * for _, v := range flagCfg.validators { parts := strings.Split(v, ":") if len(parts) != 2 { - return nil, fmt.Errorf("invalid format for validator, expected key:power, received: %s", v) + return nil, fmt.Errorf("invalid format for validator, expected key#keyType:power, received: %s", v) } - keyParts := strings.Split(parts[0], "#") - hexPub, err := hex.DecodeString(keyParts[0]) + power, err := strconv.ParseInt(parts[1], 10, 64) if err != nil { - return nil, fmt.Errorf("invalid public key for validator: %s", parts[0]) + return nil, fmt.Errorf("invalid power for validator: %s", parts[1]) } - power, err := strconv.ParseInt(parts[1], 10, 64) + keyType := crypto.KeyTypeSecp256k1 + keyParts := strings.Split(parts[0], "#") + + if len(keyParts) > 2 { + return nil, fmt.Errorf("invalid format for validator, expected key#keyType:power, received: %s", v) + } else if len(keyParts) == 2 { + keyType, err = crypto.ParseKeyType(keyParts[1]) + if err != nil { + return nil, fmt.Errorf("invalid key type for validator: %s", keyParts[1]) + } + } + + hexPub, err := hex.DecodeString(keyParts[0]) if err != nil { - return nil, fmt.Errorf("invalid power for validator: %s", parts[1]) + return nil, fmt.Errorf("invalid public key for validator: %s", parts[0]) } - keyType, err := crypto.ParseKeyType(keyParts[1]) + _, err = crypto.UnmarshalPublicKey(hexPub, keyType) if err != nil { - return nil, fmt.Errorf("invalid key type for validator: %s", keyParts[1]) + return nil, fmt.Errorf("invalid public key for validator: %s", parts[0]) } conf.Validators = append(conf.Validators, &types.Validator{ diff --git a/app/setup/testnet.go b/app/setup/testnet.go index c04ee699d..aa11962fc 100644 --- a/app/setup/testnet.go +++ b/app/setup/testnet.go @@ -1,10 +1,8 @@ package setup import ( - "crypto/sha256" - "encoding/binary" + "crypto/rand" "fmt" - "math/rand/v2" "net" "os" "path/filepath" @@ -130,7 +128,6 @@ type ConfigOpts struct { DnsHost bool } -// TODO: once changes to the tests are complete, this may not be needed func GenerateTestnetConfigs(cfg *TestnetConfig, opts *ConfigOpts, gencfg *config.GenesisConfig) error { if len(cfg.Hostnames) > 0 && len(cfg.Hostnames) != cfg.NumVals+cfg.NumNVals { return fmt.Errorf("if set, the number of hostnames %d must be equal to number of validators + number of non-validators %d", @@ -148,15 +145,10 @@ func GenerateTestnetConfigs(cfg *TestnetConfig, opts *ConfigOpts, gencfg *config return err } + // generate Keys, so that the connection strings and the validator set can be generated before the node config files are generated var keys []*crypto.Secp256k1PrivateKey - // generate the configuration for the nodes - for i := range cfg.NumVals + cfg.NumNVals { - // generate Keys, so that the connection strings and the validator set can be generated before the node config files are generated - var seed [32]byte - binary.LittleEndian.PutUint64(seed[:], cfg.StartingPort+uint64(i)) - seed = sha256.Sum256(seed[:]) - rr := rand.NewChaCha8(seed) - priv := node.NewKey(&deterministicPRNG{ChaCha8: rr}) + for range cfg.NumVals + cfg.NumNVals { + priv := node.NewKey(rand.Reader) keys = append(keys, priv) } @@ -291,33 +283,6 @@ func GenerateNodeRoot(ncfg *NodeGenConfig) error { return GenerateNodeDir(ncfg.RootDir, ncfg.Genesis, cfg, ncfg.NodeKey, "") } -type deterministicPRNG struct { - readBuf [8]byte - readLen int // 0 <= readLen <= 8 - *rand.ChaCha8 -} - -// Read is a bad replacement for the actual Read method added in Go 1.23 -func (dr *deterministicPRNG) Read(p []byte) (n int, err error) { - // fill p by calling Uint64 in a loop until we have enough bytes - if dr.readLen > 0 { - n = copy(p, dr.readBuf[len(dr.readBuf)-dr.readLen:]) - dr.readLen -= n - p = p[n:] - } - for len(p) >= 8 { - binary.LittleEndian.PutUint64(p, dr.ChaCha8.Uint64()) - p = p[8:] - n += 8 - } - if len(p) > 0 { - binary.LittleEndian.PutUint64(dr.readBuf[:], dr.Uint64()) - n += copy(p, dr.readBuf[:]) - dr.readLen = 8 - len(p) - } - return n, nil -} - type TestnetNodeConfig struct { // Config is the node configuration. Config *config.Config diff --git a/config/config.go b/config/config.go index 6c8748615..cea44a469 100644 --- a/config/config.go +++ b/config/config.go @@ -133,6 +133,18 @@ func (gc *GenesisConfig) SanityChecks() error { if val.Power <= 0 { return fmt.Errorf("Genesis validators should have non-zero power") } + + // ensure that the key type is valid + _, ok := crypto.KeyTypeDefinition(val.KeyType) + if !ok { + return fmt.Errorf("invalid key type: %s", val.KeyType) + } + + // ensure that the pubkey is valid + _, err := crypto.UnmarshalPublicKey(val.Identifier, val.KeyType) + if err != nil { + return fmt.Errorf("invalid public key: %s error: %s", val.Identifier, err) + } } if gc.InitialHeight < 0 {