1
0

BIP-0350: use bech32m for witness version 1+ addresses

We have supported sending to any witness version since Electrum 3.0, using
addresses as specified in BIP-0173 (bech32 encoding).
BIP-0350 makes a breaking change in address encoding, and recommends using
(and using only) a new encoding (bech32m) for sending to witness version 1
and later. The address encoding for currently in use witness v0 addresses
remains the same, as in BIP-0173; following the BIP-0350 spec.

closes https://github.com/spesmilo/electrum/issues/6949

related:
cd3885c0fb/bip-0350.mediawiki
https://github.com/bitcoin/bitcoin/pull/20861
This commit is contained in:
SomberNight
2021-03-17 18:11:55 +01:00
parent 468636eae0
commit 4315fa4371
8 changed files with 216 additions and 77 deletions

View File

@@ -13,6 +13,7 @@ import bitstring
from .bitcoin import hash160_to_b58_address, b58_address_to_hash160, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC
from .segwit_addr import bech32_encode, bech32_decode, CHARSET
from . import segwit_addr
from . import constants
from . import ecc
from .bitcoin import COIN
@@ -81,18 +82,14 @@ def bitarray_to_u5(barr):
ret.append(s.read(5).uint)
return ret
def encode_fallback(fallback, currency):
def encode_fallback(fallback: str, currency):
""" Encode all supported fallback addresses.
"""
if currency in [constants.BitcoinMainnet.SEGWIT_HRP, constants.BitcoinTestnet.SEGWIT_HRP]:
fbhrp, witness = bech32_decode(fallback, ignore_long_length=True)
if fbhrp:
if fbhrp != currency:
raise ValueError("Not a bech32 address for this currency")
wver = witness[0]
if wver > 16:
raise ValueError("Invalid witness version {}".format(witness[0]))
wprog = u5_to_bitarray(witness[1:])
wver, wprog_ints = segwit_addr.decode_segwit_address(currency, fallback)
if wver is not None:
wprog = bytes(wprog_ints)
else:
addrtype, addr = b58_address_to_hash160(fallback)
if is_p2pkh(currency, addrtype):
@@ -106,6 +103,7 @@ def encode_fallback(fallback, currency):
else:
raise NotImplementedError("Support for currency {} not implemented".format(currency))
def parse_fallback(fallback, currency):
if currency in [constants.BitcoinMainnet.SEGWIT_HRP, constants.BitcoinTestnet.SEGWIT_HRP]:
wver = fallback[0:5].uint
@@ -114,7 +112,10 @@ def parse_fallback(fallback, currency):
elif wver == 18:
addr=hash160_to_b58_address(fallback[5:].tobytes(), base58_prefix_map[currency][1])
elif wver <= 16:
addr=bech32_encode(currency, bitarray_to_u5(fallback))
witprog = fallback[5:] # cut witver
witprog = witprog[:len(witprog) // 8 * 8] # can only be full bytes
witprog = witprog.tobytes()
addr = segwit_addr.encode_segwit_address(currency, wver, witprog)
else:
return None
else:
@@ -262,7 +263,7 @@ def lnencode(addr: 'LnAddr', privkey) -> str:
sig = bytes(sig[1:]) + recovery_flag
data += sig
return bech32_encode(hrp, bitarray_to_u5(data))
return bech32_encode(segwit_addr.Encoding.BECH32, hrp, bitarray_to_u5(data))
class LnAddr(object):
def __init__(self, *, paymenthash: bytes = None, amount=None, currency=None, tags=None, date=None,
@@ -365,9 +366,13 @@ class SerializableKey:
def lndecode(invoice: str, *, verbose=False, expected_hrp=None) -> LnAddr:
if expected_hrp is None:
expected_hrp = constants.net.SEGWIT_HRP
hrp, data = bech32_decode(invoice, ignore_long_length=True)
if not hrp:
decoded_bech32 = bech32_decode(invoice, ignore_long_length=True)
hrp = decoded_bech32.hrp
data = decoded_bech32.data
if decoded_bech32.encoding is None:
raise ValueError("Bad bech32 checksum")
if decoded_bech32.encoding != segwit_addr.Encoding.BECH32:
raise ValueError("Bad bech32 encoding: must be using vanilla BECH32")
# BOLT #11:
#