Skip to content
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

testnet setup command to generate non-deterministic private keys #1442

Merged
merged 1 commit into from
Mar 4, 2025
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
27 changes: 19 additions & 8 deletions app/setup/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
43 changes: 4 additions & 39 deletions app/setup/testnet.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package setup

import (
"crypto/sha256"
"encoding/binary"
"crypto/rand"
"fmt"
"math/rand/v2"
"net"
"os"
"path/filepath"
Expand Down Expand Up @@ -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",
Expand All @@ -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)
}

Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading