latest versions
This commit is contained in:
148
btcphrasechecker/bitcoin/derivation.go
Normal file
148
btcphrasechecker/bitcoin/derivation.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package bitcoin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"btcphrasechecker/types"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
)
|
||||
|
||||
// DerivationPath represents a BIP derivation path configuration
|
||||
type DerivationPath struct {
|
||||
Name string
|
||||
Path string
|
||||
Purpose uint32
|
||||
Desc string
|
||||
}
|
||||
|
||||
// Standard Bitcoin derivation paths
|
||||
var StandardPaths = []DerivationPath{
|
||||
{
|
||||
Name: "BIP44 (Legacy)",
|
||||
Path: "m/44'/0'/0'/0",
|
||||
Purpose: 44,
|
||||
Desc: "P2PKH addresses (1...)",
|
||||
},
|
||||
{
|
||||
Name: "BIP49 (SegWit)",
|
||||
Path: "m/49'/0'/0'/0",
|
||||
Purpose: 49,
|
||||
Desc: "P2SH-wrapped SegWit (3...)",
|
||||
},
|
||||
{
|
||||
Name: "BIP84 (Native SegWit)",
|
||||
Path: "m/84'/0'/0'/0",
|
||||
Purpose: 84,
|
||||
Desc: "Bech32 addresses (bc1...)",
|
||||
},
|
||||
}
|
||||
|
||||
// DeriveAddresses derives Bitcoin addresses from a seed for all standard paths
|
||||
func DeriveAddresses(seed []byte, addressCount int) ([]types.AddressInfo, error) {
|
||||
masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create master key: %w", err)
|
||||
}
|
||||
|
||||
var allAddresses []types.AddressInfo
|
||||
|
||||
for _, pathInfo := range StandardPaths {
|
||||
addresses, err := derivePathAddresses(masterKey, pathInfo, addressCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allAddresses = append(allAddresses, addresses...)
|
||||
}
|
||||
|
||||
return allAddresses, nil
|
||||
}
|
||||
|
||||
// derivePathAddresses derives addresses for a specific derivation path
|
||||
func derivePathAddresses(masterKey *hdkeychain.ExtendedKey, pathInfo DerivationPath, count int) ([]types.AddressInfo, error) {
|
||||
var addresses []types.AddressInfo
|
||||
|
||||
// Derive base path: m/purpose'/coin_type'/account'/change
|
||||
// For Bitcoin: coin_type = 0, account = 0, change = 0 (external addresses)
|
||||
key := masterKey
|
||||
key, _ = key.Derive(hdkeychain.HardenedKeyStart + pathInfo.Purpose)
|
||||
key, _ = key.Derive(hdkeychain.HardenedKeyStart + 0) // coin_type = 0 for Bitcoin
|
||||
key, _ = key.Derive(hdkeychain.HardenedKeyStart + 0) // account = 0
|
||||
key, _ = key.Derive(0) // change = 0 (external)
|
||||
|
||||
// Derive individual addresses
|
||||
for i := uint32(0); i < uint32(count); i++ {
|
||||
childKey, err := key.Derive(i)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
pubKey, err := childKey.ECPubKey()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
pubKeyBytes := pubKey.SerializeCompressed()
|
||||
address, err := deriveAddress(pubKeyBytes, pathInfo.Purpose)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
addresses = append(addresses, types.AddressInfo{
|
||||
Address: address,
|
||||
Path: fmt.Sprintf("%s/%d", pathInfo.Path, i),
|
||||
Chain: types.ChainBitcoin,
|
||||
})
|
||||
}
|
||||
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
// deriveAddress creates an address from a public key based on the purpose
|
||||
func deriveAddress(pubKeyBytes []byte, purpose uint32) (string, error) {
|
||||
|
||||
switch purpose {
|
||||
case 44:
|
||||
// Legacy P2PKH
|
||||
addr, err := btcutil.NewAddressPubKey(pubKeyBytes, &chaincfg.MainNetParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return addr.EncodeAddress(), nil
|
||||
|
||||
case 49:
|
||||
// P2SH-wrapped SegWit (BIP49)
|
||||
// Create witness program for P2WPKH
|
||||
pubKeyHash := btcutil.Hash160(pubKeyBytes)
|
||||
witnessProgram, err := txscript.NewScriptBuilder().
|
||||
AddOp(txscript.OP_0).
|
||||
AddData(pubKeyHash).
|
||||
Script()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Wrap witness program in P2SH
|
||||
scriptAddr, err := btcutil.NewAddressScriptHash(witnessProgram, &chaincfg.MainNetParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return scriptAddr.EncodeAddress(), nil
|
||||
|
||||
case 84:
|
||||
// Native SegWit (bech32)
|
||||
addr, err := btcutil.NewAddressWitnessPubKeyHash(
|
||||
btcutil.Hash160(pubKeyBytes),
|
||||
&chaincfg.MainNetParams,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return addr.EncodeAddress(), nil
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported purpose: %d", purpose)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user