def witness_from_address(b32addr: String) -> Tuple[int, bytes, str]: "Return the witness from a bech32 native SegWit address." if isinstance(b32addr, str): b32addr = b32addr.strip() # the following check was originally in b32decode # but it does not pertain there if len(b32addr) > 90: raise BTClibValueError( f"invalid bech32 address length: {len(b32addr)} > 90") hrp, data = b32decode(b32addr) # check that it is a known SegWit address type network = network_from_key_value("hrp", hrp) if network is None: raise BTClibValueError(f"invalid hrp: {hrp}") if len(data) == 0: raise BTClibValueError(f"empty data in bech32 address: {b32addr!r}") wit_ver = data[0] wit_prg = bytes(power_of_2_base_conversion(data[1:], 5, 8, False)) return wit_ver, check_witness(wit_ver, wit_prg), network
def b58decode( cls: Type["BIP32KeyData"], address: String, check_validity: bool = True ) -> "BIP32KeyData": if isinstance(address, str): address = address.strip() xkey_bin = base58.b58decode(address) # pylance cannot grok the following line return cls.parse(xkey_bin, check_validity) # type: ignore
def b64decode(cls: Type[_Psbt], psbt_str: String, check_validity: bool = True) -> _Psbt: if isinstance(psbt_str, str): psbt_str = psbt_str.strip() psbt_decoded = base64.b64decode(psbt_str) # pylance cannot grok the following line return cls.parse(psbt_decoded, check_validity) # type: ignore
def b64decode(cls: Type["Sig"], data: String, check_validity: bool = True) -> "Sig": """Return the verified components of the provided BMS signature. The address-based BMS signature can be represented as (rf, r, s) tuple or as base64-encoding of the compact format [1-byte rf][32-bytes r][32-bytes s]. """ if isinstance(data, str): data = data.strip() data_decoded = base64.b64decode(data) # pylance cannot grok the following line return cls.parse(data_decoded, check_validity) # type: ignore
def h160_from_address(b58addr: String) -> Tuple[str, bytes, str]: "Return the payload from a base58 address." if isinstance(b58addr, str): b58addr = b58addr.strip() payload = b58decode(b58addr, 21) prefix = payload[:1] for script_type in ("p2pkh", "p2sh"): # with pytohn>=3.8 use walrus operator # if network := network_from_key_value(script_type, prefix): network = network_from_key_value(script_type, prefix) if network: return script_type, payload[1:], network err_msg = f"invalid base58 address prefix: 0x{prefix.hex()}" raise BTClibValueError(err_msg)
def _prv_keyinfo_from_wif(wif: String, network: Optional[str] = None, compressed: Optional[bool] = None) -> PrvkeyInfo: """Return private key tuple(int, compressed, network) from a WIF. WIF is always compressed and includes network information: here the 'network, compressed' input parameters are passed only to allow consistency checks. """ if isinstance(wif, str): wif = wif.strip() payload = b58decode(wif) net = network_from_key_value("wif", payload[:1]) if net is None: raise BTClibValueError(f"invalid wif prefix: {payload[:1]!r}") if network is not None and net != network: raise BTClibValueError(f"not a {network} wif: {wif!r}") ec = NETWORKS[net].curve if len(payload) == ec.n_size + 2: # compressed WIF compr = True if payload[-1] != 0x01: # must have a trailing 0x01 raise BTClibValueError( "not a compressed WIF: missing trailing 0x01") prv_key = payload[1:-1] elif len(payload) == ec.n_size + 1: # uncompressed WIF compr = False prv_key = payload[1:] else: raise BTClibValueError(f"wrong WIF size: {len(payload)}") if compressed is not None and compr != compressed: raise BTClibValueError("compression requirement mismatch") q = int.from_bytes(prv_key, byteorder="big") if not 0 < q < ec.n: raise BTClibValueError(f"private key {hex(q)} not in [1, n-1]") return q, net, compr
def b32decode(bech: String) -> Tuple[str, List[int]]: "Validate a bech32 string, and determine HRP and data." if isinstance(bech, str): bech = bech.strip() if isinstance(bech, bytes): bech = bech.decode("ascii") if not all(47 < ord(x) < 123 for x in bech): raise BTClibValueError(f"ASCII character outside [48-122]: {bech}") if bech.lower() != bech and bech.upper() != bech: raise BTClibValueError(f"mixed case: {bech}") bech = bech.lower() # it is fine to limit bech32 _bitcoin_addresses_ at 90 chars, # but it should be enforced when working with addresses, # not here at bech32 level. # e.g. Lightning Network uses bech32 without such limitation # if len(bech) > 90: # raise BTClibValueError(f"Bech32 string length ({len(bech)}) > 90") pos = bech.rfind("1") # find the separator between hrp and data if pos == -1: raise BTClibValueError(f"missing HRP: {bech}") if pos == 0: raise BTClibValueError(f"empty HRP: {bech}") if pos + 7 > len(bech): raise BTClibValueError(f"too short checksum: {bech}") hrp = bech[:pos] if any(x not in _ALPHABET for x in bech[pos + 1:]): raise BTClibValueError(f"invalid data characters: {bech}") data = [_ALPHABET.find(x) for x in bech[pos + 1:]] if _verify_checksum(hrp, data): return hrp, data[:-6] raise BTClibValueError(f"invalid checksum: {bech}")
def witness_from_address(b32addr: String) -> Tuple[int, bytes, str]: """Return the witness from a bech32 native SegWit address. The returned data structure is: version, program, network. """ if isinstance(b32addr, str): b32addr = b32addr.strip() # the following check was originally in b32decode # but it does not pertain there if len(b32addr) > 90: raise BTClibValueError( f"invalid bech32 address length: {len(b32addr)} > 90") hrp, data, checksum = __b32decode(b32addr) if len(data) == 0: raise BTClibValueError(f"empty data in bech32 address: {b32addr!r}") wit_ver = data[0] wit_prog = bytes(power_of_2_base_conversion(data[1:], 5, 8, False)) wit_prog = check_witness(wit_ver, wit_prog) if wit_ver == 0: if not b32_verify_checksum(hrp, data + checksum): raise BTClibValueError(f"invalid checksum: {b32addr!r}") else: if not bech32m_verify_checksum(hrp, data + checksum): raise BTClibValueError(f"invalid checksum: {b32addr!r}") # check that it is a known SegWit address type network = network_from_key_value("hrp", hrp) if network is None: raise BTClibValueError(f"invalid hrp: {hrp}") return wit_ver, wit_prog, network
def has_segwit_prefix(addr: String) -> bool: str_addr = addr.strip().lower() if isinstance( addr, str) else addr.decode("ascii") return any( str_addr.startswith(NETWORKS[net].hrp + "1") for net in NETWORKS)