Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd: add gen-p2pkey command #316

Merged
merged 1 commit into from
Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not related, but I saw that cli-reference created for the release is incorrect.


- name: View cli reference
run: cat cli-reference.txt
Expand Down
18 changes: 6 additions & 12 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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)

Expand All @@ -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")
}
}

Expand Down
10 changes: 9 additions & 1 deletion app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"flag"
"fmt"
"net"
"strings"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -150,7 +151,14 @@ func pingCluster(t *testing.T, test pingTest) {
asserter.Await(t)
cancel()

require.NoError(t, eg.Wait())
err := eg.Wait()
if err != nil && strings.Contains(err.Error(), "bind: address already in use") {
// This sometimes happens, not sure how to lock available ports...
t.Skip("couldn't bind to available port")
return
}

require.NoError(t, err)
}

// startExtBootnode creates a new discv5 listener and returns its local enode.
Expand Down
8 changes: 7 additions & 1 deletion app/simnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,13 @@ func testSimnet(t *testing.T, args simnetArgs) {
}
})

require.NoError(t, eg.Wait())
err = eg.Wait()
if err != nil && strings.Contains(err.Error(), "bind: address already in use") {
// This sometimes happens, not sure how to lock available ports...
t.Skip("couldn't bind to available port")
return
}
require.NoError(t, err)
}

// startTeku starts a teku validator client for the provided node and returns updated args.
Expand Down
1 change: 1 addition & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func New() *cobra.Command {
return newRootCmd(
newVersionCmd(runVersionCmd),
newEnrCmd(runNewENR),
newGenP2PKeyCmd(runGenP2PKey),
newRunCmd(app.Run),
newGenSimnetCmd(runGenSimnet),
)
Expand Down
29 changes: 25 additions & 4 deletions cmd/cmd_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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 {
Expand All @@ -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
}),
newGenP2PKeyCmd(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
}),
Expand Down
12 changes: 3 additions & 9 deletions cmd/enr.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#893 was fixed here

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")
}
Expand Down
66 changes: 66 additions & 0 deletions cmd/genp2pkey.go
Original file line number Diff line number Diff line change
@@ -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 newGenP2PKeyCmd(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
}

// runGenP2PKey stores a new p2pkey to disk and prints the ENR for the provided config.
func runGenP2PKey(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
}
33 changes: 33 additions & 0 deletions cmd/genp2pkey_internal_test.go
Original file line number Diff line number Diff line change
@@ -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 = runGenP2PKey(io.Discard, p2p.Config{}, temp)
require.NoError(t, err)
}
2 changes: 1 addition & 1 deletion cmd/gen_simnet.go → cmd/gensimnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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("", "")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixing this test that was flapping

require.NoError(t, err)
defer os.RemoveAll(dir)

var buf bytes.Buffer
conf := simnetConfig{
Expand All @@ -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.
}
2 changes: 1 addition & 1 deletion cmd/testdata/TestGenSimnet
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 0 additions & 1 deletion cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 14 additions & 17 deletions p2p/k1.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe name as NewP2PKey or NewPrivKey ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NewP2PKey would stutter since the package already p2p. and NewPrivKey doesn't indicate that the key is also saved to disk.

if err := os.MkdirAll(datadir, 0o755); err != nil {
return nil, errors.Wrap(err, "mkdir")
}

Expand All @@ -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")
}
Expand Down