Skip to content

Commit

Permalink
Merge pull request #305 from harmony-one/mnemonic
Browse files Browse the repository at this point in the history
Recovery Using Mnemonic
  • Loading branch information
sunwavesun authored Oct 10, 2024
2 parents d2fd6dd + 6c0174e commit b40966e
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 5 deletions.
8 changes: 8 additions & 0 deletions cmd/subcommands/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ var (
blsFilePath string
blsShardID uint32
blsCount uint32
coinType uint32 // coin type used for key path derivation BIP-44 (1023 default for Harmony; 60 for Ethereum or for Metamask mnemonics)
keyIndex uint32
ppPrompt = fmt.Sprintf(
"prompt for passphrase, otherwise use default passphrase: \"`%s`\"", c.DefaultPassphrase,
)
Expand Down Expand Up @@ -214,7 +216,11 @@ func keysSub() []*cobra.Command {
if !bip39.IsMnemonicValid(m) {
return mnemonic.InvalidMnemonic
}

acc.Mnemonic = m
acc.CoinType = &coinType
acc.HdIndexNumber = &keyIndex

if err := account.CreateNewLocalAccount(&acc); err != nil {
return err
}
Expand All @@ -226,6 +232,8 @@ func keysSub() []*cobra.Command {
}
cmdRecoverMnemonic.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt)
cmdRecoverMnemonic.Flags().StringVar(&passphraseFilePath, "passphrase-file", "", "path to a file containing the passphrase")
cmdRecoverMnemonic.Flags().Uint32Var(&coinType, "coin-type", 1023, "coin type used for key path derivation (1023 default for Harmony; 60 for Ethereum or for Metamask mnemonics)")
cmdRecoverMnemonic.Flags().Uint32Var(&keyIndex, "index", 0, "index of the key recovered from the provided mnemonic")

cmdImportKS := &cobra.Command{
Use: "import-ks <KEYSTORE_FILE_PATH> [ACCOUNT_NAME]",
Expand Down
15 changes: 13 additions & 2 deletions pkg/account/creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Creation struct {
Mnemonic string
HdAccountNumber *uint32
HdIndexNumber *uint32
CoinType *uint32
}

func New() string {
Expand All @@ -34,8 +35,18 @@ func CreateNewLocalAccount(candidate *Creation) error {
if candidate.Mnemonic == "" {
candidate.Mnemonic = mnemonic.Generate()
}
// Hardcoded index of 0 here.
private, _ := keys.FromMnemonicSeedAndPassphrase(candidate.Mnemonic, 0)

index := uint32(0)
if candidate.HdIndexNumber != nil {
index = *candidate.HdIndexNumber
}

coinType := uint32(1023)
if candidate.CoinType != nil {
coinType = *candidate.CoinType
}

private, _ := keys.FromMnemonicSeedAndPassphrase(candidate.Mnemonic, int(index), int(coinType))
_, err := ks.ImportECDSA(private.ToECDSA(), candidate.Passphrase)
if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions pkg/keys/mnemonic.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import (
// FromMnemonicSeedAndPassphrase mimics the Harmony JS sdk in deriving the
// private, public key pair from the mnemonic, its index, and empty string password.
// Note that an index k would be the k-th key generated using the same mnemonic.
func FromMnemonicSeedAndPassphrase(mnemonic string, index int) (*secp256k1.PrivateKey, *secp256k1.PublicKey) {
func FromMnemonicSeedAndPassphrase(mnemonic string, index int, coinType int) (*secp256k1.PrivateKey, *secp256k1.PublicKey) {
seed := bip39.NewSeed(mnemonic, "")
master, ch := hd.ComputeMastersFromSeed(seed)
private, _ := hd.DerivePrivateKeyForPath(
master,
ch,
fmt.Sprintf("44'/1023'/0'/0/%d", index),
fmt.Sprintf("44'/%d'/0'/0/%d", coinType, index),
)

return secp256k1.PrivKeyFromBytes(secp256k1.S256(), private[:])
Expand Down
3 changes: 2 additions & 1 deletion pkg/keys/mnemonic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ const (
index = 0
publicKey = "0x030b64624e60c6e6758711fdf00f7e873f40a22e647a6918ba73807fab194d09ba"
privateKey = "0xda4bc68857640103942ce7dd22a9fcdb96f3cfe0380254e81352a94ac8262ed2"
coinType = 1023
)

func TestMnemonic(t *testing.T) {
private, public := FromMnemonicSeedAndPassphrase(phrase, index)
private, public := FromMnemonicSeedAndPassphrase(phrase, index, coinType)
sk, pkCompressed := func() (string, string) {
dump := EncodeHex(private, public)
return dump.PrivateKey, dump.PublicKeyCompressed
Expand Down

0 comments on commit b40966e

Please sign in to comment.