Source code for btclib_wallet.slip132

# Copyright (c) The btclib developers
# Distributed under the MIT software license, see the accompanying
# LICENSE file or https://opensource.org/license/mit for the full text.

"""SLIP132 address.

https://github.com/satoshilabs/slips/blob/master/slip-0132.md
"""

from __future__ import annotations

import contextlib
from collections.abc import Callable

from btclib import b32, b58
from btclib.alias import NetworkField
from btclib.exceptions import BTClibValueError
from btclib.key import PubKeyData
from btclib.network import (
    NETWORKS,
    Network,
    network_from_key_value,
    network_from_xkeyversion,
)

from btclib_wallet.bip32.bip32 import (
    BIP32Key,
    BIP32KeyData,
    _key_data_from_bip32_key,
    derive,
    xpub_from_xprv,
)
from btclib_wallet.bip32.der_path import DerPath

__all__ = [
    "address_from_xkey",
    "address_from_xpub",
    "p2pkh_xkey",
    "p2wpkh_p2sh_xkey",
    "p2wpkh_xkey",
]


[docs] def address_from_xkey(xkey: BIP32Key) -> str: """Return the SLIP132 base58/bech32 address. The address is always derived from the compressed public key, as this is the default public key representation in BIP32. """ with contextlib.suppress(BTClibValueError): xkey = xpub_from_xprv(xkey) return address_from_xpub(xkey)
[docs] def address_from_xpub(xpub: BIP32Key) -> str: """Return the SLIP132 base58/bech32 address. The address is always derived from the compressed public key, as this is the default public key representation in BIP32. """ xpub = _key_data_from_bip32_key(xpub) if xpub.key[0] not in {2, 3}: # this branch is reached with an xprv: never echo it, # the prefix already says what is wrong raise BTClibValueError(f"not a public key: prefix 0x{xpub.key[:1].hex()}") # NetworkField, where inference would widen the three to str: they # are field names of Network, resolved with getattr by the lookup version_list: list[NetworkField] = [ "bip32_pub", "slip132_p2wpkh_pub", "slip132_p2wpkh_p2sh_pub", ] function_list: list[Callable[[PubKeyData], str]] = [ b58.p2pkh, b32.p2wpkh, b58.p2wpkh_p2sh, ] for version, function in zip(version_list, function_list, strict=True): if network := network_from_key_value(version, xpub.version): # `BIP32KeyData.key`, which for an xpub is the SEC octets an # encoder builds an address from: an extended key is not what # one takes (issue btclib-org/btclib#1188), and the prefix check # above is what makes the field readable as a public key here. # check_validity=False: that check is the prefix, and the size is # `BIP32KeyData`'s own return function(PubKeyData(xpub.key, network, check_validity=False)) # reachable: b58decode accepts the p2wsh versions too, and a p2wsh # address is not a function of the public key alone err_msg = f"unknown xpub version: {xpub.version.hex()}" raise BTClibValueError(err_msg)
def _helper_checks( xkey: BIP32Key, check_root_xkey: bool ) -> tuple[BIP32KeyData, Network]: xkey = _key_data_from_bip32_key(xkey) if check_root_xkey and not xkey.is_root: # xkey may be an xprv: never echo it; depth and # parent fingerprint are the non-root, non-secret, parts err_msg = f"not a root key: depth {xkey.depth}" err_msg += f", parent fingerprint 0x{xkey.parent_fingerprint.hex()}" raise BTClibValueError(err_msg) network = NETWORKS[network_from_xkeyversion(xkey.version)] return xkey, network
[docs] def p2pkh_xkey( xkey: BIP32Key, der_path: DerPath = "m/44h/0h/0h", check_root_xkey: bool = True ) -> str: """Return a p2pkh BIP32 xprv/xpub key at the derivation path.""" xkey, network = _helper_checks(xkey, check_root_xkey) version = network.bip32_prv if xkey.is_private else network.bip32_pub return derive(xkey, der_path, version)
[docs] def p2wpkh_p2sh_xkey( xkey: BIP32Key, der_path: DerPath = "m/49h/0h/0h", check_root_xkey: bool = True ) -> str: """Return a p2wpkh-p2sh BIP32 yprv/ypub key at the derivation path.""" xkey, network = _helper_checks(xkey, check_root_xkey) version = ( network.slip132_p2wpkh_p2sh_prv if xkey.is_private else network.slip132_p2wpkh_p2sh_pub ) return derive(xkey, der_path, version)
[docs] def p2wpkh_xkey( xkey: BIP32Key, der_path: DerPath = "m/84h/0h/0h", check_root_xkey: bool = True ) -> str: """Return a p2wpkh BIP32 zprv/zpub master key at the derivation path.""" xkey, network = _helper_checks(xkey, check_root_xkey) version = ( network.slip132_p2wpkh_prv if xkey.is_private else network.slip132_p2wpkh_pub ) return derive(xkey, der_path, version)