func generateTestnet(r *rand.Rand, opt map[string]interface{}) (e2e.Manifest, error) {
	manifest := e2e.Manifest{
		IPv6:             ipv6.Choose(r).(bool),
		ABCIProtocol:     nodeABCIProtocols.Choose(r),
		InitialHeight:    int64(opt["initialHeight"].(int)),
		InitialState:     opt["initialState"].(map[string]string),
		Validators:       &map[string]int64{},
		ValidatorUpdates: map[string]map[string]int64{},
		Nodes:            map[string]*e2e.ManifestNode{},
		KeyType:          keyType.Choose(r).(string),
		Evidence:         evidence.Choose(r).(int),
		QueueType:        opt["queueType"].(string),
		TxSize:           int64(txSize.Choose(r).(int)),
	}
	var numSeeds, numValidators, numFulls, numLightClients int
	switch opt["topology"].(string) {
	case "single":
		numValidators = 1
	case "quad":
		numValidators = 4
	case "large":
		numSeeds = r.Intn(1)
		numLightClients = r.Intn(2)
		numValidators = 4 + r.Intn(4)
		numFulls = r.Intn(4)
	default:
		return manifest, fmt.Errorf("unknown topology %q", opt["topology"])
	}
	for i := 1; i <= numSeeds; i++ {
		node := generateNode(r, manifest, e2e.ModeSeed, 0, false)
		manifest.Nodes[fmt.Sprintf("seed%02d", i)] = node
	}
	var numSyncingNodes = 0
	nextStartAt := manifest.InitialHeight + 5
	quorum := numValidators*2/3 + 1
	for i := 1; i <= numValidators; i++ {
		startAt := int64(0)
		if i > quorum && numSyncingNodes < 2 && r.Float64() >= 0.25 {
			numSyncingNodes++
			startAt = nextStartAt
			nextStartAt += 5
		}
		name := fmt.Sprintf("validator%02d", i)
		node := generateNode(r, manifest, e2e.ModeValidator, startAt, i <= 2)
		manifest.Nodes[name] = node
		if startAt == 0 {
			(*manifest.Validators)[name] = int64(30 + r.Intn(71))
		} else {
			manifest.ValidatorUpdates[fmt.Sprint(startAt+5)] = map[string]int64{
				name: int64(30 + r.Intn(71)),
			}
		}
	}
	switch opt["validators"].(string) {
	case "genesis":
	case "initchain":
		manifest.ValidatorUpdates["0"] = *manifest.Validators
		manifest.Validators = &map[string]int64{}
	default:
		return manifest, fmt.Errorf("invalid validators option %q", opt["validators"])
	}
	for i := 1; i <= numFulls; i++ {
		startAt := int64(0)
		if numSyncingNodes < 2 && r.Float64() >= 0.5 {
			numSyncingNodes++
			startAt = nextStartAt
			nextStartAt += 5
		}
		node := generateNode(r, manifest, e2e.ModeFull, startAt, false)
		manifest.Nodes[fmt.Sprintf("full%02d", i)] = node
	}
	var seedNames, peerNames, lightProviders []string
	for name, node := range manifest.Nodes {
		if node.Mode == string(e2e.ModeSeed) {
			seedNames = append(seedNames, name)
		} else {
			if (node.StartAt == 0 || node.StartAt == manifest.InitialHeight) && node.RetainBlocks == 0 {
				lightProviders = append(lightProviders, name)
			}
			peerNames = append(peerNames, name)
		}
	}
	for _, name := range seedNames {
		for _, otherName := range seedNames {
			if name != otherName {
				manifest.Nodes[name].Seeds = append(manifest.Nodes[name].Seeds, otherName)
			}
		}
	}
	sort.Slice(peerNames, func(i, j int) bool {
		iName, jName := peerNames[i], peerNames[j]
		switch {
		case manifest.Nodes[iName].StartAt < manifest.Nodes[jName].StartAt:
			return true
		case manifest.Nodes[iName].StartAt > manifest.Nodes[jName].StartAt:
			return false
		default:
			return strings.Compare(iName, jName) == -1
		}
	})
	for i, name := range peerNames {
		if len(seedNames) > 0 &&
			manifest.Nodes[name].StateSync == e2e.StateSyncDisabled &&
			(i == 0 || r.Float64() >= 0.5) {
			manifest.Nodes[name].Seeds = uniformSetChoice(seedNames).Choose(r)
		} else if i > 1 && r.Float64() >= 0.5 {
			peers := uniformSetChoice(peerNames[:i])
			manifest.Nodes[name].PersistentPeers = peers.ChooseAtLeast(r, 2)
		}
	}
	for i := 1; i <= numLightClients; i++ {
		startAt := manifest.InitialHeight + 5
		node := generateLightNode(r, startAt+(5*int64(i)), lightProviders)
		manifest.Nodes[fmt.Sprintf("light%02d", i)] = node
	}
	return manifest, nil
}
generateTestnet generates a single testnet with the given options.
package main
import (
	"fmt"
	"math/rand"
	"sort"
	"strings"
	e2e "github.com/tendermint/tendermint/test/e2e/pkg"
	"github.com/tendermint/tendermint/types"
)
var (
	testnetCombinations = map[string][]interface{}{
		"topology":      {"single", "quad", "large"},
		"queueType":     {"priority"},  
		"initialHeight": {0, 1000},
		"initialState": {
			map[string]string{},
			map[string]string{"initial01": "a", "initial02": "b", "initial03": "c"},
		},
		"validators": {"genesis", "initchain"},
	}
	nodeDatabases = weightedChoice{
		"goleveldb": 35,
		"badgerdb":  35,
		"boltdb":    15,
		"rocksdb":   10,
		"cleveldb":  5,
	}
	nodeABCIProtocols = weightedChoice{
		"builtin": 50,
		"tcp":     20,
		"grpc":    20,
		"unix":    10,
	}
	nodePrivvalProtocols = weightedChoice{
		"file": 50,
		"grpc": 20,
		"tcp":  20,
		"unix": 10,
	}
	nodeMempools = weightedChoice{
		"v0": 20,
		"v1": 80,
	}
	nodeStateSyncs = weightedChoice{
		e2e.StateSyncDisabled: 10,
		e2e.StateSyncP2P:      45,
		e2e.StateSyncRPC:      45,
	}
	nodePersistIntervals  = uniformChoice{0, 1, 5}
	nodeSnapshotIntervals = uniformChoice{0, 5}
	nodeRetainBlocks      = uniformChoice{
		0,
		2 * int(e2e.EvidenceAgeHeight),
		4 * int(e2e.EvidenceAgeHeight),
	}
	nodePerturbations = probSetChoice{
		"disconnect": 0.1,
		"pause":      0.1,
		"kill":       0.1,
		"restart":    0.1,
	}
	evidence = uniformChoice{0, 1, 10}
	txSize   = uniformChoice{1024, 4096}  
	ipv6     = uniformChoice{false, true}
	keyType  = uniformChoice{types.ABCIPubKeyTypeEd25519, types.ABCIPubKeyTypeSecp256k1}
)
func Generate(r *rand.Rand, opts Options) ([]e2e.Manifest, error) {
	manifests := []e2e.Manifest{}
	for _, opt := range combinations(testnetCombinations) {
		manifest, err := generateTestnet(r, opt)
		if err != nil {
			return nil, err
		}
		if len(manifest.Nodes) < opts.MinNetworkSize {
			continue
		}
		if opts.MaxNetworkSize > 0 && len(manifest.Nodes) >= opts.MaxNetworkSize {
			continue
		}
		manifests = append(manifests, manifest)
	}
	return manifests, nil
}
type Options struct {
	MinNetworkSize int
	MaxNetworkSize int
	NumGroups      int
	Directory      string
	Reverse        bool
}