Skip to content

Commit

Permalink
testnet setup command to generate non-determinstic
Browse files Browse the repository at this point in the history
private keys and improved runtime sanity checks for the genesis validators
setup genesis command to validate the validators pubkey
  • Loading branch information
charithabandi committed Mar 4, 2025
1 parent 4428f7c commit a255c67
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 47 deletions.
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

0 comments on commit a255c67

Please sign in to comment.