From 6cdcd5fed77b2dab8095a94478e4a4a7be3d04cf Mon Sep 17 00:00:00 2001 From: corverroos Date: Wed, 30 Mar 2022 13:52:29 +0200 Subject: [PATCH] cmd: add gen-p2pkey command --- .github/workflows/release.yml | 2 +- app/app.go | 18 ++--- cmd/cmd.go | 1 + cmd/cmd_internal_test.go | 29 ++++++-- cmd/enr.go | 12 +--- cmd/genp2p.go | 66 +++++++++++++++++++ cmd/genp2p_internal_test.go | 33 ++++++++++ cmd/{gen_simnet.go => gensimnet.go} | 2 +- ...nal_test.go => gensimnet_internal_test.go} | 9 ++- cmd/testdata/TestGenSimnet | 2 +- cmd/version.go | 1 - p2p/k1.go | 31 ++++----- 12 files changed, 155 insertions(+), 51 deletions(-) create mode 100644 cmd/genp2p.go create mode 100644 cmd/genp2p_internal_test.go rename cmd/{gen_simnet.go => gensimnet.go} (99%) rename cmd/{gen_simnet_internal_test.go => gensimnet_internal_test.go} (86%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 52fcc713f..39caf1c05 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: tags: "${{ steps.get-version.outputs.version }}" - name: Generate cli reference - run: docker run ghcr.io/obolnetwork/charon/charon:${{steps.get-version.outputs.version}} charon --help > cli-reference.txt + run: docker run ghcr.io/obolnetwork/charon/charon:${{steps.get-version.outputs.version}} charon run --help > cli-reference.txt - name: View cli reference run: cat cli-reference.txt diff --git a/app/app.go b/app/app.go index 1cc880466..2667600c4 100644 --- a/app/app.go +++ b/app/app.go @@ -127,7 +127,7 @@ func Run(ctx context.Context, conf Config) (err error) { return err } - tcpNode, localEnode, err := wireP2P(ctx, life, conf, manifest) + tcpNode, localEnode, err := wireP2P(life, conf, manifest) if err != nil { return err } @@ -140,7 +140,8 @@ func Run(ctx context.Context, conf Config) (err error) { log.Info(ctx, "Manifest loaded", z.Int("peers", len(manifest.Peers)), z.Str("peer_id", p2p.ShortID(tcpNode.ID())), - z.Int("peer_index", nodeIdx.PeerIdx)) + z.Int("peer_index", nodeIdx.PeerIdx), + z.Str("enr", localEnode.Node().String())) wireMonitoringAPI(life, conf.MonitoringAddr, localEnode) @@ -153,21 +154,14 @@ func Run(ctx context.Context, conf Config) (err error) { } // wireP2P constructs the p2p tcp (libp2p) and udp (discv5) nodes and registers it with the life cycle manager. -func wireP2P(ctx context.Context, life *lifecycle.Manager, conf Config, manifest Manifest, +func wireP2P(life *lifecycle.Manager, conf Config, manifest Manifest, ) (host.Host, *enode.LocalNode, error) { p2pKey := conf.TestConfig.P2PKey if p2pKey == nil { var err error - var loaded bool - p2pKey, loaded, err = p2p.LoadOrCreatePrivKey(conf.DataDir) + p2pKey, err = p2p.LoadPrivKey(conf.DataDir) if err != nil { - return nil, nil, errors.Wrap(err, "load or create peer ID") - } - - if loaded { - log.Info(ctx, "Loaded p2p key", z.Str("dir", conf.DataDir)) - } else { - log.Info(ctx, "Generated new p2p key", z.Str("dir", conf.DataDir)) + return nil, nil, errors.Wrap(err, "load p2p key") } } diff --git a/cmd/cmd.go b/cmd/cmd.go index 9e3252f9e..02fe1aecb 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -41,6 +41,7 @@ func New() *cobra.Command { return newRootCmd( newVersionCmd(runVersionCmd), newEnrCmd(runNewENR), + newGenP2PCmd(runGenP2P), newRunCmd(app.Run), newGenSimnetCmd(runGenSimnet), ) diff --git a/cmd/cmd_internal_test.go b/cmd/cmd_internal_test.go index 225ebaad8..294232ea2 100644 --- a/cmd/cmd_internal_test.go +++ b/cmd/cmd_internal_test.go @@ -35,7 +35,9 @@ func TestCmdFlags(t *testing.T) { Name string Args []string VersionConfig *versionConfig - appConfig *app.Config + AppConfig *app.Config + P2PConfig *p2p.Config + Datadir string }{ { Name: "version verbose", @@ -50,7 +52,7 @@ func TestCmdFlags(t *testing.T) { { Name: "run command", Args: slice("run"), - appConfig: &app.Config{ + AppConfig: &app.Config{ Log: log.Config{ Level: "info", Format: "console", @@ -71,6 +73,18 @@ func TestCmdFlags(t *testing.T) { JaegerService: "charon", }, }, + { + Name: "gen p2p", + Args: slice("gen-p2pkey"), + Datadir: "./charon/data", + P2PConfig: &p2p.Config{ + UDPAddr: "127.0.0.1:30309", + TCPAddrs: []string{"127.0.0.1:13900"}, + Allowlist: "", + Denylist: "", + DBPath: "", + }, + }, } for _, test := range tests { @@ -81,8 +95,15 @@ func TestCmdFlags(t *testing.T) { require.Equal(t, *test.VersionConfig, config) }), newRunCmd(func(_ context.Context, config app.Config) error { - require.NotNil(t, test.appConfig) - require.Equal(t, *test.appConfig, config) + require.NotNil(t, test.AppConfig) + require.Equal(t, *test.AppConfig, config) + + return nil + }), + newGenP2PCmd(func(_ io.Writer, config p2p.Config, datadir string) error { + require.NotNil(t, test.P2PConfig) + require.Equal(t, *test.P2PConfig, config) + require.Equal(t, test.Datadir, datadir) return nil }), diff --git a/cmd/enr.go b/cmd/enr.go index 93a45e0ab..0476b889f 100644 --- a/cmd/enr.go +++ b/cmd/enr.go @@ -46,20 +46,14 @@ func newEnrCmd(runFunc func(io.Writer, p2p.Config, string) error) *cobra.Command return cmd } -// Function for printing status of ENR for this instance. +// runNewENR loads the p2pkey from disk and prints the ENR for the provided config. func runNewENR(w io.Writer, config p2p.Config, dataDir string) error { - identityKey, loaded, err := p2p.LoadOrCreatePrivKey(dataDir) + key, err := p2p.LoadPrivKey(dataDir) if err != nil { return err } - if loaded { - _, _ = fmt.Fprintf(w, "Loaded p2p key from folder %s", dataDir) - } else { - _, _ = fmt.Fprintf(w, "Generated new p2p key to folder %s", dataDir) - } - - localEnode, db, err := p2p.NewLocalEnode(config, identityKey) + localEnode, db, err := p2p.NewLocalEnode(config, key) if err != nil { return errors.Wrap(err, "failed to open peer DB") } diff --git a/cmd/genp2p.go b/cmd/genp2p.go new file mode 100644 index 000000000..6f48cbd1e --- /dev/null +++ b/cmd/genp2p.go @@ -0,0 +1,66 @@ +// Copyright © 2021 Obol Technologies Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + "io" + + "github.com/spf13/cobra" + + "github.com/obolnetwork/charon/app/errors" + "github.com/obolnetwork/charon/p2p" +) + +func newGenP2PCmd(runFunc func(io.Writer, p2p.Config, string) error) *cobra.Command { + var ( + config p2p.Config + dataDir string + ) + + cmd := &cobra.Command{ + Use: "gen-p2pkey", + Short: "Generates a new p2p key", + Long: `Generates a new p2p authentication key (ecdsa-k1) and saves it to the data directory`, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + return runFunc(cmd.OutOrStdout(), config, dataDir) + }, + } + + bindGeneralFlags(cmd.Flags(), &dataDir) + bindP2PFlags(cmd.Flags(), &config) + + return cmd +} + +// runGenP2P stores a new p2pkey to disk and prints the ENR for the provided config. +func runGenP2P(w io.Writer, config p2p.Config, dataDir string) error { + key, err := p2p.NewSavedPrivKey(dataDir) + if err != nil { + return err + } + + localEnode, db, err := p2p.NewLocalEnode(config, key) + if err != nil { + return errors.Wrap(err, "failed to open peer DB") + } + defer db.Close() + + _, _ = fmt.Fprintf(w, "Created key: %s/p2pkey\n", dataDir) + _, _ = fmt.Fprintln(w, localEnode.Node().String()) + + return nil +} diff --git a/cmd/genp2p_internal_test.go b/cmd/genp2p_internal_test.go new file mode 100644 index 000000000..f646cc53e --- /dev/null +++ b/cmd/genp2p_internal_test.go @@ -0,0 +1,33 @@ +// Copyright © 2021 Obol Technologies Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "io" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/obolnetwork/charon/p2p" +) + +func TestRunGenP2P(t *testing.T) { + temp, err := os.MkdirTemp("", "") + require.NoError(t, err) + + err = runGenP2P(io.Discard, p2p.Config{}, temp) + require.NoError(t, err) +} diff --git a/cmd/gen_simnet.go b/cmd/gensimnet.go similarity index 99% rename from cmd/gen_simnet.go rename to cmd/gensimnet.go index 953190c70..31c40796d 100644 --- a/cmd/gen_simnet.go +++ b/cmd/gensimnet.go @@ -220,7 +220,7 @@ func newPeer(clusterDir, nodeDir, charonBin string, peerIdx int, nextPort func() Port: nextPort(), } - p2pKey, _, err := p2p.LoadOrCreatePrivKey(nodeDir) + p2pKey, err := p2p.NewSavedPrivKey(nodeDir) if err != nil { return p2p.Peer{}, errors.Wrap(err, "create p2p key") } diff --git a/cmd/gen_simnet_internal_test.go b/cmd/gensimnet_internal_test.go similarity index 86% rename from cmd/gen_simnet_internal_test.go rename to cmd/gensimnet_internal_test.go index b1df5faca..28a33a3cb 100644 --- a/cmd/gen_simnet_internal_test.go +++ b/cmd/gensimnet_internal_test.go @@ -27,11 +27,8 @@ import ( //go:generate go test . -run=TestGenSimnet -update func TestGenSimnet(t *testing.T) { - dir := "testdata/simnet" - require.NoError(t, os.RemoveAll(dir)) - err := os.MkdirAll(dir, 0o755) + dir, err := os.MkdirTemp("", "") require.NoError(t, err) - defer os.RemoveAll(dir) var buf bytes.Buffer conf := simnetConfig{ @@ -45,7 +42,9 @@ func TestGenSimnet(t *testing.T) { err = runGenSimnet(&buf, conf) require.NoError(t, err) - testutil.RequireGoldenBytes(t, buf.Bytes()) + out := buf.Bytes() + out = bytes.Replace(out, []byte(dir), []byte("charon-simnet"), 1) + testutil.RequireGoldenBytes(t, out) // TODO(corver): Assert generated files. } diff --git a/cmd/testdata/TestGenSimnet b/cmd/testdata/TestGenSimnet index 0621c7df9..2a2cabd81 100644 --- a/cmd/testdata/TestGenSimnet +++ b/cmd/testdata/TestGenSimnet @@ -1,7 +1,7 @@ Referencing charon binary in scripts: charon Created a simnet cluster: -testdata/simnet/ +charon-simnet/ ├─ manifest.json Cluster manifest defines the cluster; used by all nodes ├─ run_cluster.sh Convenience script to run all nodes ├─ teamocil.yml Configuration for teamocil utility to show output in different tmux panes diff --git a/cmd/version.go b/cmd/version.go index 1662a2cb3..f461c32c9 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -59,7 +59,6 @@ func runVersionCmd(out io.Writer, config versionConfig) { } buildInfo, ok := debug.ReadBuildInfo() - if !ok { _, _ = fmt.Fprintf(out, "\nFailed to gather build info") return diff --git a/p2p/k1.go b/p2p/k1.go index fcb47bce5..94ac89c36 100644 --- a/p2p/k1.go +++ b/p2p/k1.go @@ -18,32 +18,29 @@ import ( "crypto/ecdsa" "os" "path" - "path/filepath" "github.com/ethereum/go-ethereum/crypto" "github.com/obolnetwork/charon/app/errors" ) -// LoadOrCreatePrivKey returns a k1 (secp256k1) private key and true from the provided folder. -// If it doesn't exist, a new key is generated and stored and returned with false. -func LoadOrCreatePrivKey(dataDir string) (*ecdsa.PrivateKey, bool, error) { - keyPath := path.Join(dataDir, "p2pkey") - - key, err := crypto.LoadECDSA(keyPath) - if errors.Is(err, os.ErrNotExist) { - key, err = newSavedPrivKey(keyPath) - return key, false, err - } else if err != nil { - return nil, false, errors.Wrap(err, "load key") +func p2pKeyPath(datadir string) string { + return path.Join(datadir, "p2pkey") +} + +// LoadPrivKey returns the ecdsa k1 key saved in the directory. +func LoadPrivKey(dataDir string) (*ecdsa.PrivateKey, error) { + key, err := crypto.LoadECDSA(p2pKeyPath(dataDir)) + if err != nil { + return nil, errors.Wrap(err, "load key") } - return key, true, nil + return key, nil } -// newSavedPrivKey generates a new key and saves the new node identity. -func newSavedPrivKey(keyPath string) (*ecdsa.PrivateKey, error) { - if err := os.MkdirAll(filepath.Dir(keyPath), 0o755); err != nil { +// NewSavedPrivKey generates a new ecdsa k1 key and saves it to the directory. +func NewSavedPrivKey(datadir string) (*ecdsa.PrivateKey, error) { + if err := os.MkdirAll(datadir, 0o755); err != nil { return nil, errors.Wrap(err, "mkdir") } @@ -52,7 +49,7 @@ func newSavedPrivKey(keyPath string) (*ecdsa.PrivateKey, error) { return nil, errors.Wrap(err, "gen key") } - err = crypto.SaveECDSA(keyPath, key) + err = crypto.SaveECDSA(p2pKeyPath(datadir), key) if err != nil { return nil, errors.Wrap(err, "save key") }