From 38581f9a9e9d468613dcc1bcf6df528533425292 Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Wed, 11 Sep 2024 17:57:21 -0700 Subject: [PATCH 1/2] feat: support bip39 mnemonic for importing keys --- pkg/keys/create.go | 26 ++++++++++++++------------ pkg/keys/error.go | 3 +++ pkg/keys/import.go | 29 ++++++++++++++++++++++------- pkg/keys/import_test.go | 21 ++++++++++++++++++--- 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/pkg/keys/create.go b/pkg/keys/create.go index 977f1ea..1261ca3 100644 --- a/pkg/keys/create.go +++ b/pkg/keys/create.go @@ -79,7 +79,8 @@ This command will create keys in $HOME/.eigenlayer/operator_keys/ location switch keyType { case KeyTypeECDSA: - privateKey, mnemonic, err := generateEcdsaKeyWithMnemonic() + // Passing empty string to generate a new mnemonic + privateKey, mnemonic, err := generateEcdsaKeyWithMnemonic("") if err != nil { return err } @@ -98,17 +99,18 @@ This command will create keys in $HOME/.eigenlayer/operator_keys/ location return createCmd } -func generateEcdsaKeyWithMnemonic() (*ecdsa.PrivateKey, string, error) { - // Generate entropy for mnemonic - entropy, err := bip39.NewEntropy(128) - if err != nil { - return nil, "", fmt.Errorf("failed to generate entropy: %v", err) - } - - // Generate mnemonic - mnemonic, err := bip39.NewMnemonic(entropy) - if err != nil { - return nil, "", fmt.Errorf("failed to generate mnemonic: %v", err) +func generateEcdsaKeyWithMnemonic(mnemonic string) (*ecdsa.PrivateKey, string, error) { + if mnemonic == "" { + // Generate entropy for mnemonic + entropy, err := bip39.NewEntropy(128) + if err != nil { + return nil, "", fmt.Errorf("failed to generate entropy: %v", err) + } + // Generate mnemonic + mnemonic, err = bip39.NewMnemonic(entropy) + if err != nil { + return nil, "", fmt.Errorf("failed to generate mnemonic: %v", err) + } } // Create HD wallet diff --git a/pkg/keys/error.go b/pkg/keys/error.go index f4daf41..0cbd503 100644 --- a/pkg/keys/error.go +++ b/pkg/keys/error.go @@ -11,4 +11,7 @@ var ( ErrInvalidKeyType = errors.New("invalid key type. key type must be either 'ecdsa' or 'bls'") ErrInvalidPassword = errors.New("invalid password") ErrInvalidHexPrivateKey = errors.New("invalid hex private key") + ErrInvalidKeyFormat = errors.New( + "invalid key format. Please provide a single hex encoded private key or a 12-word mnemonic", + ) ) diff --git a/pkg/keys/import.go b/pkg/keys/import.go index 09b7c8d..c37551c 100644 --- a/pkg/keys/import.go +++ b/pkg/keys/import.go @@ -1,9 +1,11 @@ package keys import ( + "crypto/ecdsa" "fmt" "math/big" "regexp" + "strings" "github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common" "github.com/Layr-Labs/eigenlayer-cli/pkg/telemetry" @@ -53,8 +55,13 @@ This command will import keys in $HOME/.eigenlayer/operator_keys/ location } privateKey := args.Get(1) - if err := validatePrivateKey(privateKey); err != nil { - return err + if privateKey == "" { + return ErrEmptyPrivateKey + } + + pkSlice := strings.Split(privateKey, " ") + if len(pkSlice) != 1 && len(pkSlice) != 12 { + return ErrInvalidKeyFormat } // Check if input is available in the pipe and read the password from it @@ -65,12 +72,20 @@ This command will import keys in $HOME/.eigenlayer/operator_keys/ location switch keyType { case KeyTypeECDSA: - privateKey = common.Trim0x(privateKey) - privateKeyPair, err := crypto.HexToECDSA(privateKey) - if err != nil { - return err + var privateKeyPair *ecdsa.PrivateKey + var err error + if len(pkSlice) == 1 { + privateKey = common.Trim0x(privateKey) + privateKeyPair, err = crypto.HexToECDSA(privateKey) + if err != nil { + return err + } + } else { + privateKeyPair, _, err = generateEcdsaKeyWithMnemonic(privateKey) + if err != nil { + return err + } } - // TODO: Add support for mnemonic imports return saveEcdsaKey(keyName, p, privateKeyPair, insecure, stdInPassword, readFromPipe, "") case KeyTypeBLS: privateKeyBigInt := new(big.Int) diff --git a/pkg/keys/import_test.go b/pkg/keys/import_test.go index 533e1e8..d2f49af 100644 --- a/pkg/keys/import_test.go +++ b/pkg/keys/import_test.go @@ -11,7 +11,6 @@ import ( "github.com/Layr-Labs/eigensdk-go/crypto/bls" - "github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common" prompterMock "github.com/Layr-Labs/eigenlayer-cli/pkg/utils/mocks" "github.com/stretchr/testify/assert" @@ -67,7 +66,7 @@ func TestImportCmd(t *testing.T) { { name: "keyname with whitespaces", args: []string{"--key-type", "ecdsa", "hello", "hello world"}, - err: ErrPrivateKeyContainsWhitespaces, + err: ErrInvalidKeyFormat, }, { name: "invalid key type", @@ -127,6 +126,22 @@ func TestImportCmd(t *testing.T) { expectedPrivKey: "6842fb8f5fa574d0482818b8a825a15c4d68f542693197f2c2497e3562f335f6", keyPath: filepath.Join(homePath, OperatorKeystoreSubFolder, "/test.ecdsa.key.json"), }, + { + name: "valid ecdsa key import with mnemonic", + args: []string{ + "--key-type", + "ecdsa", + "test", + "kidney various problem toe ready mass exhibit volume shuffle must glue sketch", + }, + err: nil, + promptMock: func(p *prompterMock.MockPrompter) { + p.EXPECT().InputHiddenString(gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil) + p.EXPECT().InputHiddenString(gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil) + }, + expectedPrivKey: "aee7f88721a86c9e269f50ba9a8675609ee8eef54947827fcdce818d8aafd3b1", + keyPath: filepath.Join(homePath, OperatorKeystoreSubFolder, "/test.ecdsa.key.json"), + }, { name: "valid bls key import", args: []string{ @@ -202,7 +217,7 @@ func TestImportCmd(t *testing.T) { if tt.args[1] == KeyTypeECDSA { key, err := GetECDSAPrivateKey(tt.keyPath, "") assert.NoError(t, err) - assert.Equal(t, common.Trim0x(tt.args[3]), hex.EncodeToString(key.D.Bytes())) + assert.Equal(t, tt.expectedPrivKey, hex.EncodeToString(key.D.Bytes())) } else if tt.args[1] == KeyTypeBLS { key, err := bls.ReadPrivateKeyFromFile(tt.keyPath, "") assert.NoError(t, err) From 82951b27fc6905141c16537b476ed3d5e636a756 Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Wed, 11 Sep 2024 17:59:00 -0700 Subject: [PATCH 2/2] remove unused code --- pkg/keys/import.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pkg/keys/import.go b/pkg/keys/import.go index c37551c..6904236 100644 --- a/pkg/keys/import.go +++ b/pkg/keys/import.go @@ -4,7 +4,6 @@ import ( "crypto/ecdsa" "fmt" "math/big" - "regexp" "strings" "github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common" @@ -120,15 +119,3 @@ This command will import keys in $HOME/.eigenlayer/operator_keys/ location } return importCmd } - -func validatePrivateKey(pk string) error { - if len(pk) == 0 { - return ErrEmptyPrivateKey - } - - if match, _ := regexp.MatchString("\\s", pk); match { - return ErrPrivateKeyContainsWhitespaces - } - - return nil -}