mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
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:
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
Reference in New Issue
Block a user