Accounts V2: Remove Mnemonic Confirm From Direct Keymanager (#6696)

* remove mnemonic from direct
* Merge refs/heads/master into remove-mnemonic-direct
This commit is contained in:
Raul Jordan
2020-07-22 17:52:31 -05:00
committed by GitHub
parent 7d80415089
commit cab89c37f1
9 changed files with 22 additions and 161 deletions

View File

@@ -6,7 +6,6 @@ go_library(
srcs = [
"direct.go",
"doc.go",
"mnemonic.go",
],
importpath = "github.com/prysmaticlabs/prysm/validator/keymanager/v2/direct",
visibility = [
@@ -22,27 +21,21 @@ go_library(
"//validator/accounts/v2/iface:go_default_library",
"//validator/keymanager/v2:go_default_library",
"@com_github_google_uuid//:go_default_library",
"@com_github_manifoldco_promptui//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_tyler_smith_go_bip39//:go_default_library",
"@com_github_wealdtech_go_eth2_wallet_encryptor_keystorev4//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"direct_test.go",
"mnemonic_test.go",
],
srcs = ["direct_test.go"],
embed = [":go_default_library"],
deps = [
"//proto/validator/accounts/v2:go_default_library",
"//shared/bls:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/depositutil:go_default_library",
"//shared/testutil:go_default_library",
"//shared/testutil/assert:go_default_library",
"//shared/testutil/require:go_default_library",
@@ -51,7 +44,6 @@ go_test(
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@com_github_tyler_smith_go_bip39//:go_default_library",
"@com_github_wealdtech_go_eth2_wallet_encryptor_keystorev4//:go_default_library",
],
)

View File

@@ -46,11 +46,10 @@ type Config struct {
// Keymanager implementation for direct keystores utilizing EIP-2335.
type Keymanager struct {
wallet iface.Wallet
cfg *Config
mnemonicGenerator SeedPhraseFactory
keysCache map[[48]byte]bls.SecretKey
lock sync.RWMutex
wallet iface.Wallet
cfg *Config
keysCache map[[48]byte]bls.SecretKey
lock sync.RWMutex
}
// DefaultConfig for a direct keymanager implementation.
@@ -61,13 +60,10 @@ func DefaultConfig() *Config {
}
// NewKeymanager instantiates a new direct keymanager from configuration options.
func NewKeymanager(ctx context.Context, wallet iface.Wallet, cfg *Config, skipMnemonicConfirm bool) (*Keymanager, error) {
func NewKeymanager(ctx context.Context, wallet iface.Wallet, cfg *Config) (*Keymanager, error) {
k := &Keymanager{
wallet: wallet,
cfg: cfg,
mnemonicGenerator: &EnglishMnemonicGenerator{
skipMnemonicConfirm: skipMnemonicConfirm,
},
wallet: wallet,
cfg: cfg,
keysCache: make(map[[48]byte]bls.SecretKey),
}
// If the wallet has the capability of unlocking accounts using
@@ -109,7 +105,7 @@ func MarshalConfigFile(ctx context.Context, cfg *Config) ([]byte, error) {
// CreateAccount for a direct keymanager implementation. This utilizes
// the EIP-2335 keystore standard for BLS12-381 keystores. It
// stores the generated keystore.json file in the wallet and additionally
// generates a mnemonic for withdrawal credentials. At the end, it logs
// generates withdrawal credentials. At the end, it logs
// the raw deposit data hex string for users to copy.
func (dr *Keymanager) CreateAccount(ctx context.Context, password string) (string, error) {
// Create a new, unique account name and write its password + directory to disk.
@@ -128,14 +124,17 @@ func (dr *Keymanager) CreateAccount(ctx context.Context, password string) (strin
// Generate a withdrawal key and confirm user
// acknowledgement of a 256-bit entropy mnemonic phrase.
withdrawalKey := bls.RandKey()
rawWithdrawalKey := withdrawalKey.Marshal()[:]
seedPhrase, err := dr.mnemonicGenerator.Generate(rawWithdrawalKey)
if err != nil {
return "", errors.Wrap(err, "could not generate mnemonic for withdrawal key")
}
if err := dr.mnemonicGenerator.ConfirmAcknowledgement(seedPhrase); err != nil {
return "", errors.Wrap(err, "could not confirm acknowledgement of mnemonic")
}
log.Info(
"Write down the private key, as it is your unique " +
"withdrawal private key for eth2",
)
fmt.Printf(`
==========================Withdrawal Key===========================
%#x
===================================================================
`, withdrawalKey.Marshal())
// Upon confirmation of the withdrawal key, proceed to display
// and write associated deposit data to disk.

View File

@@ -12,46 +12,23 @@ import (
validatorpb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/depositutil"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
mock "github.com/prysmaticlabs/prysm/validator/accounts/v2/testing"
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
logTest "github.com/sirupsen/logrus/hooks/test"
"github.com/tyler-smith/go-bip39"
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
)
type mockMnemonicGenerator struct {
generatedMnemonics []string
}
func (m *mockMnemonicGenerator) Generate(data []byte) (string, error) {
newMnemonic, err := bip39.NewMnemonic(data)
if err != nil {
return "", err
}
m.generatedMnemonics = append(m.generatedMnemonics, newMnemonic)
return newMnemonic, nil
}
func (m *mockMnemonicGenerator) ConfirmAcknowledgement(phrase string) error {
return nil
}
func TestKeymanager_CreateAccount(t *testing.T) {
hook := logTest.NewGlobal()
wallet := &mock.Wallet{
Files: make(map[string]map[string][]byte),
AccountPasswords: make(map[string]string),
}
mnemonicGenerator := &mockMnemonicGenerator{
generatedMnemonics: make([]string, 0),
}
dr := &Keymanager{
wallet: wallet,
mnemonicGenerator: mnemonicGenerator,
wallet: wallet,
}
ctx := context.Background()
password := "secretPassw0rd$1999"
@@ -92,24 +69,6 @@ func TestKeymanager_CreateAccount(t *testing.T) {
)
}
// We ensure the mnemonic phrase has successfully been generated.
require.Equal(t, 1, len(mnemonicGenerator.generatedMnemonics), "Expected to have generated new mnemonic for private key")
mnemonicPhrase := mnemonicGenerator.generatedMnemonics[0]
rawWithdrawalBytes, err := bip39.EntropyFromMnemonic(mnemonicPhrase)
require.NoError(t, err)
validatorWithdrawalKey, err := bls.SecretKeyFromBytes(rawWithdrawalBytes)
require.NoError(t, err, "Could not instantiate bls secret key from bytes")
// We then verify the withdrawal hash created from the recovered withdrawal key
// given the mnemonic phrase does indeed verify with the deposit data that was persisted on disk.
withdrawalHash := depositutil.WithdrawalCredentialsHash(validatorWithdrawalKey)
if !bytes.Equal(withdrawalHash, depositData.WithdrawalCredentials) {
t.Errorf(
"Expected matching withdrawal credentials, got %#x, received %#x",
withdrawalHash,
depositData.WithdrawalCredentials,
)
}
testutil.AssertLogsContain(t, hook, "Successfully created new validator account")
}

View File

@@ -1,65 +0,0 @@
package direct
import (
"fmt"
"github.com/manifoldco/promptui"
"github.com/tyler-smith/go-bip39"
)
// SeedPhraseFactory defines a struct which
// can generate new seed phrases in human-readable
// format from a source of entropy in raw bytes. It
// also provides methods for verifying a user has successfully
// acknowledged the mnemonic phrase and written it down offline.
type SeedPhraseFactory interface {
Generate(data []byte) (string, error)
ConfirmAcknowledgement(phrase string) error
}
// EnglishMnemonicGenerator implements methods for creating
// mnemonic seed phrases in english using a given
// source of entropy such as a private key.
type EnglishMnemonicGenerator struct {
skipMnemonicConfirm bool
}
// Generate a mnemonic seed phrase in english using a source of
// entropy given as raw bytes.
func (m *EnglishMnemonicGenerator) Generate(data []byte) (string, error) {
return bip39.NewMnemonic(data)
}
// ConfirmAcknowledgement displays the mnemonic phrase to the user
// and confirms the user has written down the phrase securely offline.
func (m *EnglishMnemonicGenerator) ConfirmAcknowledgement(phrase string) error {
log.Info(
"Write down the sentence below, as it is your only " +
"means of recovering your withdrawal key",
)
fmt.Printf(`
=================Withdrawal Key Recovery Phrase====================
%s
===================================================================
`, phrase)
if m.skipMnemonicConfirm {
return nil
}
// Confirm the user has written down the mnemonic phrase offline.
prompt := promptui.Prompt{
Label: "Confirm you have written down the recovery words somewhere safe (offline)",
IsConfirm: true,
}
expected := "y"
var result string
var err error
for result != expected {
result, err = prompt.Run()
if err != nil {
log.Errorf("Could not confirm acknowledgement of prompt, please enter y")
}
}
return nil
}

View File

@@ -1,20 +0,0 @@
package direct
import (
"testing"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
"github.com/tyler-smith/go-bip39"
)
func TestMnemonic_Generate_CanRecover(t *testing.T) {
generator := &EnglishMnemonicGenerator{}
data := make([]byte, 32)
copy(data, []byte("hello-world"))
phrase, err := generator.Generate(data)
require.NoError(t, err)
entropy, err := bip39.EntropyFromMnemonic(phrase)
require.NoError(t, err)
assert.DeepEqual(t, data, entropy, "Expected to recover original data")
}