From e2be4d4f7481de291ddfa05c25965c6631ae8bd6 Mon Sep 17 00:00:00 2001 From: Tre Fong Date: Sat, 11 Jul 2026 16:44:24 -0500 Subject: [PATCH] fix(bip32): add EncodePrivateWIF for classic WIF (#2) EncodeWIF historically returns BIP32 extended keys (dgpv/dgub), not classic Wallet Import Format. Document that and add EncodePrivateWIF() which returns dumpprivkey-style compressed WIF for private keys. Tips DOGE: DTRJECKXfxgnSzGbt7TYRikLPDocqvdmo5 --- bip32.go | 16 +++++++++++++++- bip32_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/bip32.go b/bip32.go index 180e9f6..c3fa079 100644 --- a/bip32.go +++ b/bip32.go @@ -51,11 +51,25 @@ func (key *Bip32Key) ChildNumber() uint32 { return key.child_number } -// EncodeWIF encodes this key in Bip32 WIF format (dgpv,dgub) +// EncodeWIF encodes this key in BIP32 extended-key serialization (dgpv/dgub). +// +// Historical name: this is NOT classic Wallet Import Format (the single private +// key "WIF" used by dumpprivkey / importprivkey). For classic WIF of the EC +// private key, use EncodePrivateWIF. See https://github.com/dogeorg/doge/issues/2 func (key *Bip32Key) EncodeWIF() string { return EncodeBip32WIF(key) } +// EncodePrivateWIF returns classic Wallet Import Format (compressed pubkey +// marker) for a private Bip32Key. Returns an error if this key is public-only. +func (key *Bip32Key) EncodePrivateWIF() (string, error) { + pk, err := key.GetECPrivKey() + if err != nil { + return "", err + } + return EncodeECPrivKeyWIF(pk, key.chain), nil +} + // Public returns the Public Bip32Key corresponding to a Private Bip32Key. // If key is already a Public Bip32Key, the same *Bip32Key is returned. func (key *Bip32Key) Public() *Bip32Key { diff --git a/bip32_test.go b/bip32_test.go index c8e27a4..cb79c4d 100644 --- a/bip32_test.go +++ b/bip32_test.go @@ -1,6 +1,7 @@ package doge import ( + "bytes" "fmt" "testing" ) @@ -420,3 +421,42 @@ func TestBip32InvalidKeys(t *testing.T) { //log.Printf("%v", err) } } + +func TestBip32EncodePrivateWIF(t *testing.T) { + // EncodePrivateWIF must return classic WIF, not BIP32 extended (dgpv/dgub). + // Seed from BIP32 test vector 1. + master, err := Bip32MasterFromSeed(hx2b("000102030405060708090a0b0c0d0e0f"), &BitcoinMainChain) + if err != nil { + t.Fatalf("Bip32MasterFromSeed: %v", err) + } + classic, err := master.EncodePrivateWIF() + if err != nil { + t.Fatalf("EncodePrivateWIF: %v", err) + } + extended := master.EncodeWIF() + if classic == extended { + t.Fatalf("EncodePrivateWIF returned extended key: %s", classic) + } + // Classic WIF starts with K/L (compressed mainnet) or 5 (uncompressed); BIP32 xprv with xprv/dgpv + if len(classic) < 50 { + t.Fatalf("EncodePrivateWIF too short: %s", classic) + } + pk, chain, err := DecodeECPrivKeyWIF(classic, nil) + if err != nil { + t.Fatalf("DecodeECPrivKeyWIF failed for EncodePrivateWIF output: %v (%s)", err, classic) + } + if chain != &BitcoinMainChain { + t.Fatalf("wrong chain from decoded WIF") + } + want, err := master.GetECPrivKey() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(pk[:], want[:]) { + t.Fatalf("decoded key mismatch") + } + // Public key cannot produce classic private WIF + if _, err := master.Public().EncodePrivateWIF(); err == nil { + t.Fatalf("expected error encoding private WIF from public key") + } +}