Skip to content
Closed
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
10 changes: 10 additions & 0 deletions gno.land/cmd/gnokey/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
$> cd ./gno
$> make install_gnokey

## Validator keys

Use an existing gnokey key as a validator key by exporting it in the `priv_validator_key.json` format expected by gnoland or a remote signer:

```bash
gnokey export -key mykey -validator -output-path ./priv_validator_key.json
```

If no `-output-path` is provided, the key is written to `priv_validator_key.json` in the current directory.

Also, see the [quickstart guide](../../../docs/users/interact-with-gnokey.md).

## Manual Entropy Generation
Expand Down
16 changes: 16 additions & 0 deletions tm2/pkg/bft/privval/signer/local/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,22 @@ func GeneratePersistedFileKey(filePath string) (*FileKey, error) {
// Generate a new random FileKey.
fk := GenerateFileKey()

return PersistFileKey(filePath, fk.PrivKey)
}

// PersistFileKey persists the given private key to disk using the FileKey format.
// The provided private key is used to derive the public key and address.
func PersistFileKey(filePath string, privKey crypto.PrivKey) (*FileKey, error) {
if privKey == nil {
return nil, errInvalidPrivateKey
}

fk := &FileKey{
PrivKey: privKey,
PubKey: privKey.PubKey(),
Address: privKey.PubKey().Address(),
}

// Persist the FileKey to disk.
if err := fk.save(filePath); err != nil {
return nil, err
Expand Down
28 changes: 28 additions & 0 deletions tm2/pkg/bft/privval/signer/local/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/crypto/ed25519"
osm "github.com/gnolang/gno/tm2/pkg/os"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -86,6 +87,33 @@ func TestSave(t *testing.T) {
})
}

func TestPersistFileKey(t *testing.T) {
t.Parallel()

t.Run("nil private key", func(t *testing.T) {
t.Parallel()

fk, err := PersistFileKey("", nil)
require.Nil(t, fk)
assert.ErrorIs(t, err, errInvalidPrivateKey)
})

t.Run("persist provided key", func(t *testing.T) {
t.Parallel()

priv := ed25519.GenPrivKey()
filePath := path.Join(t.TempDir(), "validator_key.json")

fk, err := PersistFileKey(filePath, priv)
require.NoError(t, err)
require.NotNil(t, fk)

loaded, err := LoadFileKey(filePath)
require.NoError(t, err)
assert.Equal(t, fk, loaded)
})
}

func TestLoadFileKey(t *testing.T) {
t.Parallel()

Expand Down
35 changes: 33 additions & 2 deletions tm2/pkg/crypto/keys/client/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@ import (
"fmt"
"os"

"github.com/gnolang/gno/tm2/pkg/bft/privval/signer/local"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
"github.com/gnolang/gno/tm2/pkg/crypto/keys/armor"
)

const (
defaultValidatorKeyFileName = "priv_validator_key.json"
)

type ExportCfg struct {
RootCfg *BaseCfg

NameOrBech32 string
OutputPath string
AsValidator bool
}

func NewExportCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command {
Expand All @@ -28,7 +34,7 @@ func NewExportCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command {
commands.Metadata{
Name: "export",
ShortUsage: "export [flags]",
ShortHelp: "exports private key armor",
ShortHelp: "exports a private key as encrypted armor or a validator key file",
},
cfg,
func(_ context.Context, args []string) error {
Expand All @@ -49,7 +55,14 @@ func (c *ExportCfg) RegisterFlags(fs *flag.FlagSet) {
&c.OutputPath,
"output-path",
"",
"the desired output path for the armor file",
"the desired output path for the armor file or validator key",
)

fs.BoolVar(
&c.AsValidator,
"validator",
false,
"export the key as a validator private key file (priv_validator_key.json)",
)
}

Expand Down Expand Up @@ -89,6 +102,24 @@ func execExport(cfg *ExportCfg, io commands.IO) error {
return fmt.Errorf("unable to export private key, %w", err)
}

// If exporting as a validator key, persist it in the priv_validator_key.json format.
if cfg.AsValidator {
outputPath := cfg.OutputPath
if outputPath == "" {
outputPath = defaultValidatorKeyFileName
}

fk, err := local.PersistFileKey(outputPath, privateKey)
if err != nil {
return fmt.Errorf("unable to write validator key, %w", err)
}

io.Printfln("Validator private key saved at %s", outputPath)
io.Printfln("Validator address: %s", fk.Address)

return nil
}

// Get the armor encrypt password
pw, err := promptPassphrase(io, cfg.RootCfg.InsecurePasswordStdin)
if err != nil {
Expand Down
46 changes: 45 additions & 1 deletion tm2/pkg/crypto/keys/client/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"

"github.com/gnolang/gno/tm2/pkg/bft/privval/signer/local"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
"github.com/gnolang/gno/tm2/pkg/testutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// newTestKeybase generates a new test key-base
Expand Down Expand Up @@ -71,7 +74,8 @@ type testCmdKeyOptsBase struct {
type testExportKeyOpts struct {
testCmdKeyOptsBase

outputPath string
outputPath string
asValidator bool
}

// exportKey runs the private key export command
Expand All @@ -89,6 +93,7 @@ func exportKey(
},
NameOrBech32: exportOpts.keyName,
OutputPath: exportOpts.outputPath,
AsValidator: exportOpts.asValidator,
}

cmdIO := commands.NewTestIO()
Expand Down Expand Up @@ -198,6 +203,45 @@ func TestExport_ExportKey(t *testing.T) {
}
}

func TestExport_AsValidatorKey(t *testing.T) {
t.Parallel()

const (
keyName = "validator-key"
password = "password"
)

// Generate a temporary key-base directory
kb, kbHome := newTestKeybase(t)

// Add an initial key to the key base
info, err := addRandomKeyToKeybase(kb, keyName, password)
require.NoError(t, err)

outputPath := filepath.Join(t.TempDir(), "priv_validator_key.json")

assert.NoError(
t,
exportKey(
testExportKeyOpts{
testCmdKeyOptsBase: testCmdKeyOptsBase{
kbHome: kbHome,
keyName: info.GetName(),
},
outputPath: outputPath,
asValidator: true,
},
strings.NewReader(fmt.Sprintf("%s\n", password)),
),
)

fileKey, err := local.LoadFileKey(outputPath)
require.NoError(t, err)

assert.Equal(t, info.GetPubKey(), fileKey.PubKey)
assert.Equal(t, info.GetAddress(), fileKey.Address)
}

func TestExport_ExportKeyWithEmptyName(t *testing.T) {
t.Parallel()

Expand Down
Loading