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

Recover private validator key #8099

Merged
merged 15 commits into from
Dec 8, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* Updated gRPC dependency to v1.33.2
* Updated iavl dependency to v0.15-rc2
* (version) [\#7848](https://github.com/cosmos/cosmos-sdk/pull/7848) [\#7941](https://github.com/cosmos/cosmos-sdk/pull/7941) `version --long` output now shows the list of build dependencies and replaced build dependencies.
* () [\#8099](https://github.com/cosmos/cosmos-sdk/pull/8099) `init` now supports a `--recover` flag to recover the private validator key from a given mnemonic
RiccardoM marked this conversation as resolved.
Show resolved Hide resolved

### State Machine Breaking Changes

Expand Down
2 changes: 1 addition & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2l
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
github.com/tendermint/tendermint v0.34.0-rc4/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4=
github.com/tendermint/tendermint v0.34.0-rc6/go.mod h1:ugzyZO5foutZImv0Iyx/gOFCX6mjJTgbLHTwi17VDVg=
github.com/tendermint/tendermint v0.34.0 h1:eXCfMgoqVSzrjzOj6clI9GAejcHH0LvOlRjpCmMJksU=
github.com/tendermint/tendermint v0.34.0 h1:CeCs4Q9+sDhdXVLwyKexUq3HhKRBl+uaLZuRSDoL464=
RiccardoM marked this conversation as resolved.
Show resolved Hide resolved
github.com/tendermint/tendermint v0.34.0/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ=
github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI=
github.com/tendermint/tm-db v0.6.3 h1:ZkhQcKnB8/2jr5EaZwGndN4owkPsGezW2fSisS9zGbg=
Expand Down
24 changes: 23 additions & 1 deletion x/genutil/client/cli/init.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package cli

import (
"bufio"
alessio marked this conversation as resolved.
Show resolved Hide resolved
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/client/input"
"github.com/cosmos/go-bip39"
"os"
"path/filepath"

Expand All @@ -25,6 +28,9 @@ import (
const (
// FlagOverwrite defines a flag to overwrite an existing genesis JSON file.
FlagOverwrite = "overwrite"

// FlagSeed defines a flag to initialize the private validator key from a specific seed.
FlagRecover = "recover"
)

type printInfo struct {
Expand Down Expand Up @@ -78,7 +84,22 @@ func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command {
chainID = fmt.Sprintf("test-chain-%v", tmrand.Str(6))
}

nodeID, _, err := genutil.InitializeNodeValidatorFiles(config)
// Get bip39 mnemonic
var mnemonic string
recover, _ := cmd.Flags().GetBool(FlagRecover)
if recover {
inBuf := bufio.NewReader(cmd.InOrStdin())
mnemonic, err := input.GetString("Enter your bip39 mnemonic", inBuf)
if err != nil {
return err
}

if !bip39.IsMnemonicValid(mnemonic) {
return errors.New("invalid mnemonic")
}
}

nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic)
if err != nil {
return err
}
Expand Down Expand Up @@ -124,6 +145,7 @@ func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command {

cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory")
cmd.Flags().BoolP(FlagOverwrite, "o", false, "overwrite the genesis.json file")
cmd.Flags().Bool(FlagRecover, false, "provide seed phrase to recover existing key instead of creating")
cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")

return cmd
Expand Down
32 changes: 32 additions & 0 deletions x/genutil/client/cli/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"github.com/cosmos/cosmos-sdk/testutil"
"io"
"os"
"testing"
Expand Down Expand Up @@ -86,6 +87,37 @@ func TestInitCmd(t *testing.T) {

}

func TestInitRecover(t *testing.T) {
home := t.TempDir()
logger := log.NewNopLogger()
cfg, err := genutiltest.CreateDefaultTendermintConfig(home)
require.NoError(t, err)

serverCtx := server.NewContext(viper.New(), cfg, logger)
interfaceRegistry := types.NewInterfaceRegistry()
marshaler := codec.NewProtoCodec(interfaceRegistry)
clientCtx := client.Context{}.
WithJSONMarshaler(marshaler).
WithLegacyAmino(makeCodec()).
WithHomeDir(home)

ctx := context.Background()
ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx)
ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx)

cmd := genutilcli.InitCmd(testMbm, home)
mockIn := testutil.ApplyMockIODiscardOutErr(cmd)

cmd.SetArgs([]string{
"appnode-test",
fmt.Sprintf("--%s=true", genutilcli.FlagRecover),
})

// use valid mnemonic and complete recovery key generation successfully
mockIn.Reset("decide praise business actor peasant farm drastic weather extend front hurt later song give verb rhythm worry fun pond reform school tumble august one\n")
require.NoError(t, cmd.ExecuteContext(ctx))
}

func TestEmptyState(t *testing.T) {
home := t.TempDir()
logger := log.NewNopLogger()
Expand Down
18 changes: 17 additions & 1 deletion x/genutil/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package genutil

import (
"encoding/json"
"github.com/cosmos/go-bip39"
tmed25519 "github.com/tendermint/tendermint/crypto/ed25519"
"path/filepath"
"time"

Expand Down Expand Up @@ -48,6 +50,12 @@ func ExportGenesisFileWithTime(

// InitializeNodeValidatorFiles creates private validator and p2p configuration files.
func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey cryptotypes.PubKey, err error) {
return InitializeNodeValidatorFilesFromMnemonic(config, "")
}

// InitializeNodeValidatorFiles creates private validator and p2p configuration files using the given mnemonic.
// If no valid mnemonic is given, a random one will be used instead.
func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic string) (nodeID string, valPubKey cryptotypes.PubKey, err error) {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return "", nil, err
Expand All @@ -65,7 +73,15 @@ func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey
return "", nil, err
}

tmValPubKey, err := privval.LoadOrGenFilePV(pvKeyFile, pvStateFile).GetPubKey()
var filePV *privval.FilePV
if !bip39.IsMnemonicValid(mnemonic) {
filePV = privval.LoadOrGenFilePV(pvKeyFile, pvStateFile)
} else {
RiccardoM marked this conversation as resolved.
Show resolved Hide resolved
privKey := tmed25519.GenPrivKeyFromSecret([]byte(mnemonic))
filePV = privval.NewFilePV(privKey, pvKeyFile, pvStateFile)
}

tmValPubKey, err := filePV.GetPubKey()
if err != nil {
return "", nil, err
}
Expand Down