Post
Topic
Board Development & Technical Discussion
Merits 1 from 1 user
Topic OP
Deriving p2wpkh addresses (bc1) using Xpriv (dumpprivkey no-legacy fail)
by
mcdouglasx
on 31/12/2024, 16:30:17 UTC
⭐ Merited by nc50lc (1)
Sometimes we want to backup our private keys from Bitcoin Core using the dumpprivkey command, but it fails because it only supports legacy wallets and does not support descriptor wallets. The solution to this is as follows:

From the internal Bitcoin Core console, extract your xpriv with the following command:

Code:
listdescriptors true

Once we have our xpriv, we paste it into the following Python code and choose the number of addresses to derive:

Code:
import hashlib
import bip32utils

def sha256(data):
    return hashlib.sha256(data).digest()

def ripemd160(data):
    h = hashlib.new('ripemd160')
    h.update(data)
    return h.digest()

def bech32_polymod(values):
    generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
    chk = 1
    for v in values:
        b = (chk >> 25)
        chk = (chk & 0x1ffffff) << 5 ^ v
        for i in range(5):
            chk ^= generator[i] if ((b >> i) & 1) else 0
    return chk

def bech_expand(hrp):
    return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]

def bech_checksum(hrp, data):
    values = bech_expand(hrp) + data
    polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
    return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]

def bech32_encode(hrp, data):
    combined = data + bech_checksum(hrp, data)
    BECH_CHRS = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
    return hrp + '1' + ''.join([BECH_CHRS[d] for d in combined])

def cbits(data, frombits, tobits, pad=True):
    acc = 0
    bits = 0
    ret = []
    maxv = (1 << tobits) - 1
    for value in data:
        if value < 0 or (value >> frombits):
            raise ValueError("Invalid value")
        acc = (acc << frombits) | value
        bits += frombits
        while bits >= tobits:
            bits -= tobits
            ret.append((acc >> bits) & maxv)
    if pad:
        if bits:
            ret.append((acc << (tobits - bits)) & maxv)
    elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
        raise ValueError("Invalid bits")
    return ret

def bech_address(pubkey):
    sha256_r = sha256(pubkey)
    ripemdr = ripemd160(sha256_r)
    data = cbits(ripemdr, 8, 5)
    data = [0] + data
    hrp = 'bc'
    address = bech32_encode(hrp, data)
    return address


xprv = "Xprv-HERE"

derive = 20 #This sets the number of child addresses to derive


bip32_root_key_obj = bip32utils.BIP32Key.fromExtendedKey(xprv)

with open("derived_addresses.txt", "w") as file:
    for i in range(derive):
        derived_key = bip32_root_key_obj.ChildKey(84 + 0x80000000).ChildKey(0 + 0x80000000).ChildKey(0 + 0x80000000).ChildKey(0).ChildKey(i)
        priv_key = derived_key.WalletImportFormat()
        pubkey = derived_key.PublicKey().hex()
        pubkey_bytes = bytes.fromhex(pubkey)
        bech32_address = bech_address(pubkey_bytes)
        file.write(f"Private Key: {priv_key}\nPublic Key: {pubkey}\nSegWit (Bech32) Address: {bech32_address}\n\n")

A text file will be created with our private keys in WIF format.