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)?
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With