Getting Address and Private Key for Mnemonic

941 views Asked by At

I am basically trying to get public and private keys by derivation paths from seed phrases for Ethereum accounts using Golang. I have this:

package generator

import (
    "fmt"

    "github.com/tyler-smith/go-bip32"
    "github.com/tyler-smith/go-bip39"
)

type HDWallet struct{}

const BIP_PATH = "m/44'/60'/0'/0/0"

func (wallet *HDWallet) GenerateAddressAndPrivateKey(seedPhrase string) (string, string, error) {
    seed, err := bip39.NewSeedWithErrorChecking(seedPhrase, "")
    if err != nil {
        return "", "", err
    }

    masterKey, err := bip32.NewMasterKey(seed)
    if err != nil {
        return "", "", err
    }

    publicKey := masterKey.PublicKey()
    if err != nil {
        return "", "", err
    }

    return ???, ???, nil
}

I have the master key but how do I get derived accounts (public and private keys)?

2

There are 2 answers

3
norym On BEST ANSWER

In order to retrieve the public and private keys as their well-known hex strings (account keys) you need to convert your generated master private and public keys to ECDSA hex string as follows. Btw, usually you wont use the master private and public key.

package main

import (
    "crypto/ecdsa"
    "encoding/hex"
    "fmt"

    "github.com/ethereum/go-ethereum/crypto"
    "github.com/tyler-smith/go-bip32"
    "github.com/tyler-smith/go-bip39"
)

func main() {
    // Generate a mnemonic
    entropy, _ := bip39.NewEntropy(256)
    mnemonic, _ := bip39.NewMnemonic(entropy)
    fmt.Println("Mnemonic (gen): ", mnemonic)

    // Generate a Bip32 HD wallet for the mnemonic and a user supplied passphrase
    seed := bip39.NewSeed(mnemonic, "Secret Passphrase")

    masterPrivateKey, _ := bip32.NewMasterKey(seed)
    masterPublicKey := masterPrivateKey.PublicKey()
    fmt.Println("Master private key (gen): ", masterPrivateKey)
    fmt.Println("Master public key (gen): ", masterPublicKey)

    // Use Unsafe to suppress error, otherwise use crypto.ToECDSA
    ecdaPrivateKey := crypto.ToECDSAUnsafe(masterPrivateKey.Key)
    ecdaPublicKey := ecdaPrivateKey.Public().(*ecdsa.PublicKey)
    fmt.Println("ECDA Private key: ", ecdaPrivateKey.D)
    fmt.Println("ECDA Public key: ", ecdaPublicKey.X)

    privateKeyHex := fmt.Sprintf("%x", ecdaPrivateKey.D)
    publicKeyHex := fmt.Sprintf("%x", crypto.CompressPubkey(ecdaPublicKey)) // Encode a public key to the 33-byte compressed format
    fmt.Println("Private key (hex):", privateKeyHex)
    fmt.Println("Public key (hex):", publicKeyHex)
}

You can check whether the generated public key as hex string belongs to the private key hex string as follows.

    // Decode the private key and public key from hex strings
    privateKey, err := crypto.HexToECDSA(privateKeyHex)
    if err != nil {
        fmt.Println("Invalid private key:", err)
        return
    }

    publicKeyBytes, err := hex.DecodeString(publicKeyHex)
    if err != nil {
        fmt.Println("Invalid public key:", err)
        return
    }

    // Use crypto.DecompressPubkey to decode the public key bytes
    givenPublicKey, err := crypto.DecompressPubkey(publicKeyBytes)
    if err != nil {
        fmt.Println("Invalid public key:", err)
        return
    }

    // Derive the public key from the private key
    derivedPublicKey := privateKey.Public().(*ecdsa.PublicKey)

    // Compare the derived public key with the given public key
    if derivedPublicKey.X.Cmp(givenPublicKey.X) == 0 && derivedPublicKey.Y.Cmp(givenPublicKey.Y) == 0 {
        fmt.Println("The private key matches the public key.")
    } else {
        fmt.Println("The private key does not match the public key.")
    }

--EDIT--

No guarantee, but try this approach.

    // BIP44 derivation path format: m / purpose' / coin_type' / account' / change / address_index
    // Example: m/44'/0'/0'/0/0
    purposeKey, _ := masterKey.NewChildKey(bip32.FirstHardenedChild + 44)
    coinTypeKey, _ := purposeKey.NewChildKey(bip32.FirstHardenedChild)
    accountKey, _ := coinTypeKey.NewChildKey(bip32.FirstHardenedChild)
    changeKey, _ := accountKey.NewChildKey(0)
    addressKey, _ := changeKey.NewChildKey(0)
1
jonny On

To get public and private keys for Ethereum accounts from seed phrases using Golang, you need to:

Use a Go Ethereum library like go-ethereum. Convert the mnemonic phrase into a seed. Derive the keys using the appropriate derivation path for Ethereum (usually m/44'/60'/0'/0/0 for the first account). Extract the private and public keys from the derived path. Make sure to handle the keys securely.