Skip to content

Commit

Permalink
cmd: remove config flags from create cluster (#563)
Browse files Browse the repository at this point in the history
Removes generated run scripts (and flags) from the charon create cluster.

category: refactor
ticket: #546
  • Loading branch information
corverroos committed May 20, 2022
1 parent 88ae354 commit 2464f0a
Show file tree
Hide file tree
Showing 6 changed files with 12 additions and 279 deletions.
234 changes: 6 additions & 228 deletions cmd/createcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@ import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"path"
"strings"
"text/template"

eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/coinbase/kryptology/pkg/signatures/bls/bls_sig"
Expand All @@ -44,49 +42,8 @@ import (
"github.com/obolnetwork/charon/tbls/tblsconv"
)

const clusterName = "local"

const scriptTmpl = `#!/usr/bin/env bash
# This script run a charon node using the p2pkey in the
# local directory and the manifest in the parent directory
{{.CharonBin}} run \
{{range .Flags}} {{.}} \
{{end}}`

const clusterTmpl = `#!/usr/bin/env bash
# This script runs all the charon nodes in
# the sub-directories; the whole cluster.
if (type -P tmux >/dev/null && type -P teamocil >/dev/null); then
echo "Commands tmux and teamocil are installed"
tmux new-session 'teamocil --layout teamocil.yml'
else
echo "⚠️ Commands tmux and teamocil are not installed, output will be merged"
trap "exit" INT TERM ERR
trap "kill 0" EXIT
{{range .}} {{.}} &
{{end}}
wait
fi
`

const teamocilTmpl = `
windows:
- name: charon-simnet
root: /tmp/charon-simnet
layout: tiled
panes: {{range .}}
- {{.}}
{{end}}
`

const (
clusterName = "local"
defaultWithdrawalAddr = "0x0000000000000000000000000000000000000000"
defaultNetwork = "prater"
)
Expand All @@ -102,11 +59,6 @@ type clusterConfig struct {

SplitKeys bool
SplitKeysDir string

ConfigEnabled bool
ConfigSimnet bool
ConfigPortStart int
ConfigBinary string
}

func newCreateClusterCmd(runFunc func(io.Writer, clusterConfig) error) *cobra.Command {
Expand Down Expand Up @@ -137,11 +89,6 @@ func bindClusterFlags(flags *pflag.FlagSet, config *clusterConfig) {

flags.BoolVar(&config.SplitKeys, "split-existing-keys", false, "Split an existing validator's private key into a set of distributed validator private key shares. Does not re-create deposit data for this key.")
flags.StringVar(&config.SplitKeysDir, "split-keys-dir", "", "Directory containing keys to split. Expects keys in keystore-*.json and passwords in keystore-*.txt. Requires --split-existing-keys.")

flags.BoolVar(&config.ConfigEnabled, "config", false, "Enables creation of local non-docker config files.")
flags.BoolVar(&config.ConfigSimnet, "config-simnet", true, "Configures a simulated network cluster with mock beacon node and mock validator clients. It showcases a running charon in isolation. Requires --config.")
flags.StringVar(&config.ConfigBinary, "config-binary", "", "Path of the charon binary to use in the config files. Defaults to this binary if empty. Requires --config.")
flags.IntVar(&config.ConfigPortStart, "config-port-start", 16000, "Starting port number used in config files. Requires --config.")
}

func runCreateCluster(w io.Writer, conf clusterConfig) error {
Expand All @@ -160,15 +107,6 @@ func runCreateCluster(w io.Writer, conf clusterConfig) error {
return errors.Wrap(err, "mkdir")
}

if conf.ConfigBinary == "" {
// Get charon binary to include in run scripts
var err error
conf.ConfigBinary, err = os.Executable()
if err != nil {
return errors.Wrap(err, "get charon binary")
}
}

if err := validateClusterConfig(conf); err != nil {
return err
}
Expand All @@ -183,17 +121,14 @@ func runCreateCluster(w io.Writer, conf clusterConfig) error {
return err
}

// Get function to create sequential ports
nextPort := nextPortFunc(conf.ConfigPortStart)

// Generate threshold bls key shares
dvs, shareSets, err2 := getTSSShares(secrets, conf)
if err2 != nil {
return err2
}

// Create p2p peers
peers, err := createPeers(conf, nextPort, shareSets)
peers, err := createPeers(conf, shareSets)
if err != nil {
return err
}
Expand All @@ -206,16 +141,6 @@ func runCreateCluster(w io.Writer, conf clusterConfig) error {
return err
}

if conf.ConfigEnabled {
if err = writeClusterScript(conf.ClusterDir, conf.NumNodes); err != nil {
return errors.Wrap(err, "write cluster script")
}

if err = writeTeamocilYML(conf.ClusterDir, conf.NumNodes); err != nil {
return errors.Wrap(err, "write teamocil.yml")
}
}

if conf.SplitKeys {
writeWarning(w)
}
Expand Down Expand Up @@ -260,10 +185,10 @@ func signDepositDatas(secrets []*bls_sig.SecretKey, withdrawalAddr string, netwo
return resp, nil
}

func createPeers(conf clusterConfig, nextPort func() int, shareSets [][]*bls_sig.SecretKeyShare) ([]p2p.Peer, error) {
func createPeers(conf clusterConfig, shareSets [][]*bls_sig.SecretKeyShare) ([]p2p.Peer, error) {
var peers []p2p.Peer
for i := 0; i < conf.NumNodes; i++ {
peer, err := newPeer(conf, i, nextPort)
peer, err := newPeer(conf, i)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -439,17 +364,7 @@ func newLock(conf clusterConfig, dvs []tbls.TSS, peers []p2p.Peer) (cluster.Lock
}

// newPeer returns a new peer, generating a p2pkey and ENR and node directory and run script in the process.
func newPeer(conf clusterConfig, peerIdx int, nextPort func() int) (p2p.Peer, error) {
tcp := net.TCPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: nextPort(),
}

udp := net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: nextPort(),
}

func newPeer(conf clusterConfig, peerIdx int) (p2p.Peer, error) {
dir := nodeDir(conf.ClusterDir, peerIdx)

p2pKey, err := p2p.NewSavedPrivKey(dir)
Expand All @@ -458,11 +373,6 @@ func newPeer(conf clusterConfig, peerIdx int, nextPort func() int) (p2p.Peer, er
}

var r enr.Record
r.Set(enr.IPv4(tcp.IP))
r.Set(enr.TCP(tcp.Port))
r.Set(enr.UDP(udp.Port))
r.SetSeq(0)

err = enode.SignV4(&r, p2pKey)
if err != nil {
return p2p.Peer{}, errors.Wrap(err, "enode sign")
Expand All @@ -473,12 +383,6 @@ func newPeer(conf clusterConfig, peerIdx int, nextPort func() int) (p2p.Peer, er
return p2p.Peer{}, errors.Wrap(err, "new peer")
}

if conf.ConfigEnabled {
if err := writeRunScript(conf, dir, nextPort(), tcp.String(), udp.String(), nextPort()); err != nil {
return p2p.Peer{}, errors.Wrap(err, "write run script")
}
}

return peer, nil
}

Expand All @@ -487,150 +391,24 @@ func writeOutput(out io.Writer, conf clusterConfig) {
var sb strings.Builder
_, _ = sb.WriteString("Created charon cluster:\n")
_, _ = sb.WriteString(fmt.Sprintf(" --split-existing-keys=%v\n", conf.SplitKeys))
_, _ = sb.WriteString(fmt.Sprintf(" --config=%v\n", conf.ConfigEnabled))
if conf.ConfigEnabled {
_, _ = sb.WriteString(fmt.Sprintf(" --config-simnet=%v\n", conf.ConfigSimnet))
_, _ = sb.WriteString(fmt.Sprintf(" --config-binary=%v\n", conf.ConfigBinary))
}
_, _ = sb.WriteString("\n")
_, _ = sb.WriteString(strings.TrimSuffix(conf.ClusterDir, "/") + "/\n")
_, _ = sb.WriteString("├─ manifest.json\tCluster manifest defines the cluster; used by all nodes\n")
_, _ = sb.WriteString("├─ deposit-data.json\tDeposit data file is used to activate a Distributed Validator on DV Launchpad\n")

if conf.ConfigEnabled {
_, _ = sb.WriteString("├─ run_cluster.sh\tConvenience script to run all nodes\n")
_, _ = sb.WriteString("├─ teamocil.yml\t\tTeamocil config for splitting logs in tmux panes\n")
}
_, _ = sb.WriteString(fmt.Sprintf("├─ node[0-%d]/\t\tDirectory for each node\n", conf.NumNodes-1))
_, _ = sb.WriteString("│ ├─ p2pkey\t\tP2P networking private key for node authentication\n")
_, _ = sb.WriteString("│ ├─ keystore-*.json\tValidator private share key for duty signing\n")
_, _ = sb.WriteString("│ ├─ keystore-*.txt\tKeystore password files for keystore-*.json\n")
if conf.ConfigEnabled {
_, _ = sb.WriteString("│ ├─ run.sh\t\tConfig script to run the node\n")
}

_, _ = fmt.Fprint(out, sb.String())
}

// writeRunScript creates run script for a node.
func writeRunScript(conf clusterConfig, nodeDir string, monitoringPort int,
tcpAddr string, udpAddr string, validatorAPIPort int,
) error {
f, err := os.Create(nodeDir + "/run.sh")
if err != nil {
return errors.Wrap(err, "create run.sh")
}
defer f.Close()

// Flags for running a node
var flags []string
flags = append(flags, fmt.Sprintf("--data-dir=\"%s\"", nodeDir))
flags = append(flags, fmt.Sprintf("--manifest-file=\"%s/manifest.json\"", conf.ClusterDir))
flags = append(flags, fmt.Sprintf("--monitoring-address=\"127.0.0.1:%d\"", monitoringPort))
flags = append(flags, fmt.Sprintf("--validator-api-address=\"127.0.0.1:%d\"", validatorAPIPort))
flags = append(flags, fmt.Sprintf("--p2p-tcp-address=%s", tcpAddr))
flags = append(flags, fmt.Sprintf("--p2p-udp-address=%s", udpAddr))
if conf.ConfigSimnet {
flags = append(flags, "--simnet-beacon-mock")
flags = append(flags, "--simnet-validator-mock")
}

tmpl, err := template.New("").Parse(scriptTmpl)
if err != nil {
return errors.Wrap(err, "new template")
}

err = tmpl.Execute(f, struct {
CharonBin string
Flags []string
}{CharonBin: conf.ConfigBinary, Flags: flags})
if err != nil {
return errors.Wrap(err, "execute template")
}

err = os.Chmod(nodeDir+"/run.sh", 0o755)
if err != nil {
return errors.Wrap(err, "change permissions")
}

return nil
}

// writeClusterScript creates script to run all the nodes in the cluster.
func writeClusterScript(clusterDir string, n int) error {
var cmds []string
for i := 0; i < n; i++ {
cmds = append(cmds, fmt.Sprintf("%s/node%d/run.sh", clusterDir, i))
}

f, err := os.Create(clusterDir + "/run_cluster.sh")
if err != nil {
return errors.Wrap(err, "create run cluster")
}

tmpl, err := template.New("").Parse(clusterTmpl)
if err != nil {
return errors.Wrap(err, "new template")
}

err = tmpl.Execute(f, cmds)
if err != nil {
return errors.Wrap(err, "execute template")
}

err = os.Chmod(clusterDir+"/run_cluster.sh", 0o755)
if err != nil {
return errors.Wrap(err, "change permissions")
}

return nil
}

// writeTeamocilYML creates teamocil configurations file to show the nodes output in different tmux panes.
func writeTeamocilYML(clusterDir string, n int) error {
var cmds []string
for i := 0; i < n; i++ {
cmds = append(cmds, fmt.Sprintf("%s/node%d/run.sh", clusterDir, i))
}

f, err := os.Create(clusterDir + "/teamocil.yml")
if err != nil {
return errors.Wrap(err, "create teamocil.yml")
}

tmpl, err := template.New("").Parse(teamocilTmpl)
if err != nil {
return errors.Wrap(err, "new template")
}

err = tmpl.Execute(f, cmds)
if err != nil {
return errors.Wrap(err, "execute template")
}

err = os.Chmod(clusterDir+"/teamocil.yml", 0o644)
if err != nil {
return errors.Wrap(err, "change permissions")
}

return nil
}

// nodeDir returns a node directory.
func nodeDir(clusterDir string, i int) string {
return fmt.Sprintf("%s/node%d", clusterDir, i)
}

// nextPortFunc returns a next port function starting at start port.
func nextPortFunc(startPort int) func() int {
port := startPort
return func() int {
port++
return port
}
}

// checksumAddr returns a valid EIP55-compliant checksummed ethereum address.
// checksumAddr returns a valid EIP55-compliant checksummed ethereum address. Returns an error if a valid address cannot be constructed.
func checksumAddr(a string) (string, error) {
if !common.IsHexAddress(a) {
return "", errors.New("invalid address")
Expand Down
Loading

0 comments on commit 2464f0a

Please sign in to comment.