package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
e2e "github.com/tendermint/tendermint/test/e2e/pkg"
|
|
)
|
|
|
|
func Start(ctx context.Context, testnet *e2e.Testnet) error {
|
|
if len(testnet.Nodes) == 0 {
|
|
return fmt.Errorf("no nodes in testnet")
|
|
}
|
|
|
|
// Nodes are already sorted by name. Sort them by name then startAt,
|
|
// which gives the overall order startAt, mode, name.
|
|
nodeQueue := testnet.Nodes
|
|
sort.SliceStable(nodeQueue, func(i, j int) bool {
|
|
a, b := nodeQueue[i], nodeQueue[j]
|
|
switch {
|
|
case a.Mode == b.Mode:
|
|
return false
|
|
case a.Mode == e2e.ModeSeed:
|
|
return true
|
|
case a.Mode == e2e.ModeValidator && b.Mode == e2e.ModeFull:
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
|
|
sort.SliceStable(nodeQueue, func(i, j int) bool {
|
|
return nodeQueue[i].StartAt < nodeQueue[j].StartAt
|
|
})
|
|
|
|
if nodeQueue[0].StartAt > 0 {
|
|
return fmt.Errorf("no initial nodes in testnet")
|
|
}
|
|
|
|
// Start initial nodes (StartAt: 0)
|
|
logger.Info("Starting initial network nodes...")
|
|
for len(nodeQueue) > 0 && nodeQueue[0].StartAt == 0 {
|
|
node := nodeQueue[0]
|
|
nodeQueue = nodeQueue[1:]
|
|
if err := execCompose(testnet.Dir, "up", "-d", node.Name); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := func() error {
|
|
ctx, cancel := context.WithTimeout(ctx, time.Minute)
|
|
defer cancel()
|
|
|
|
_, err := waitForNode(ctx, node, 0)
|
|
return err
|
|
}(); err != nil {
|
|
return err
|
|
}
|
|
node.HasStarted = true
|
|
logger.Info(fmt.Sprintf("Node %v up on http://127.0.0.1:%v", node.Name, node.ProxyPort))
|
|
}
|
|
|
|
networkHeight := testnet.InitialHeight
|
|
|
|
// Wait for initial height
|
|
logger.Info("Waiting for initial height",
|
|
"height", networkHeight,
|
|
"nodes", len(testnet.Nodes)-len(nodeQueue),
|
|
"pending", len(nodeQueue))
|
|
|
|
block, blockID, err := waitForHeight(ctx, testnet, networkHeight)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, node := range nodeQueue {
|
|
if node.StartAt > networkHeight {
|
|
// if we're starting a node that's ahead of
|
|
// the last known height of the network, then
|
|
// we should make sure that the rest of the
|
|
// network has reached at least the height
|
|
// that this node will start at before we
|
|
// start the node.
|
|
|
|
logger.Info("Waiting for network to advance to height",
|
|
"node", node.Name,
|
|
"last_height", networkHeight,
|
|
"waiting_for", node.StartAt,
|
|
"size", len(testnet.Nodes)-len(nodeQueue),
|
|
"pending", len(nodeQueue))
|
|
|
|
networkHeight = node.StartAt
|
|
|
|
block, blockID, err = waitForHeight(ctx, testnet, networkHeight)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Update any state sync nodes with a trusted height and hash
|
|
if node.StateSync != e2e.StateSyncDisabled || node.Mode == e2e.ModeLight {
|
|
err = UpdateConfigStateSync(node, block.Height, blockID.Hash.Bytes())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := execCompose(testnet.Dir, "up", "-d", node.Name); err != nil {
|
|
return err
|
|
}
|
|
|
|
wctx, wcancel := context.WithTimeout(ctx, 8*time.Minute)
|
|
status, err := waitForNode(wctx, node, node.StartAt)
|
|
if err != nil {
|
|
wcancel()
|
|
return err
|
|
}
|
|
wcancel()
|
|
|
|
node.HasStarted = true
|
|
|
|
var lastNodeHeight int64
|
|
|
|
// If the node is a light client, we fetch its current height
|
|
if node.Mode == e2e.ModeLight {
|
|
lastNodeHeight = status.LightClientInfo.LastTrustedHeight
|
|
} else {
|
|
lastNodeHeight = status.SyncInfo.LatestBlockHeight
|
|
}
|
|
logger.Info(fmt.Sprintf("Node %v up on http://127.0.0.1:%v at height %v",
|
|
node.Name, node.ProxyPort, lastNodeHeight))
|
|
}
|
|
|
|
return nil
|
|
}
|