Browse Source

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
patch-4
SomberNight 4 years ago
parent
commit
4315fa4371
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 10
      electrum/bitcoin.py
  2. 31
      electrum/lnaddr.py
  3. 8
      electrum/lnutil.py
  4. 71
      electrum/segwit_addr.py
  5. 144
      electrum/tests/test_bitcoin.py
  6. 16
      electrum/tests/test_bolt11.py
  7. 11
      electrum/tests/test_transaction.py
  8. 2
      electrum/transaction.py

10
electrum/bitcoin.py

@ -395,7 +395,9 @@ def public_key_to_p2pkh(public_key: bytes, *, net=None) -> str:
def hash_to_segwit_addr(h: bytes, witver: int, *, net=None) -> str:
if net is None: net = constants.net
return segwit_addr.encode(net.SEGWIT_HRP, witver, h)
addr = segwit_addr.encode_segwit_address(net.SEGWIT_HRP, witver, h)
assert addr is not None
return addr
def public_key_to_p2wpkh(public_key: bytes, *, net=None) -> str:
if net is None: net = constants.net
@ -452,7 +454,7 @@ def address_to_script(addr: str, *, net=None) -> str:
if net is None: net = constants.net
if not is_address(addr, net=net):
raise BitcoinException(f"invalid bitcoin address: {addr}")
witver, witprog = segwit_addr.decode(net.SEGWIT_HRP, addr)
witver, witprog = segwit_addr.decode_segwit_address(net.SEGWIT_HRP, addr)
if witprog is not None:
if not (0 <= witver <= 16):
raise BitcoinException(f'impossible witness version: {witver}')
@ -482,7 +484,7 @@ def address_to_hash(addr: str, *, net=None) -> Tuple[OnchainOutputType, bytes]:
if net is None: net = constants.net
if not is_address(addr, net=net):
raise BitcoinException(f"invalid bitcoin address: {addr}")
witver, witprog = segwit_addr.decode(net.SEGWIT_HRP, addr)
witver, witprog = segwit_addr.decode_segwit_address(net.SEGWIT_HRP, addr)
if witprog is not None:
if witver != 0:
raise BitcoinException(f"not implemented handling for witver={witver}")
@ -714,7 +716,7 @@ def address_from_private_key(sec: str) -> str:
def is_segwit_address(addr: str, *, net=None) -> bool:
if net is None: net = constants.net
try:
witver, witprog = segwit_addr.decode(net.SEGWIT_HRP, addr)
witver, witprog = segwit_addr.decode_segwit_address(net.SEGWIT_HRP, addr)
except Exception as e:
return False
return witprog is not None

31
electrum/lnaddr.py

@ -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:
#

8
electrum/lnutil.py

@ -1165,7 +1165,13 @@ class LNPeerAddr:
def get_compressed_pubkey_from_bech32(bech32_pubkey: str) -> bytes:
hrp, data_5bits = segwit_addr.bech32_decode(bech32_pubkey)
decoded_bech32 = segwit_addr.bech32_decode(bech32_pubkey)
hrp = decoded_bech32.hrp
data_5bits = 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")
if hrp != 'ln':
raise Exception('unexpected hrp: {}'.format(hrp))
data_8bits = segwit_addr.convertbits(data_5bits, 5, 8, False)

71
electrum/segwit_addr.py

@ -19,12 +19,29 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Reference implementation for Bech32 and segwit addresses."""
"""Reference implementation for Bech32/Bech32m and segwit addresses."""
from enum import Enum
from typing import Tuple, Optional, Sequence, NamedTuple, List
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
_CHARSET_INVERSE = {x: CHARSET.find(x) for x in CHARSET}
BECH32_CONST = 1
BECH32M_CONST = 0x2bc830a3
class Encoding(Enum):
"""Enumeration type to list the various supported encodings."""
BECH32 = 1
BECH32M = 2
class DecodedBech32(NamedTuple):
encoding: Optional[Encoding]
hrp: Optional[str]
data: Optional[Sequence[int]] # 5-bit ints
def bech32_polymod(values):
"""Internal function that computes the Bech32 checksum."""
@ -45,42 +62,50 @@ def bech32_hrp_expand(hrp):
def bech32_verify_checksum(hrp, data):
"""Verify a checksum given HRP and converted data characters."""
return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1
check = bech32_polymod(bech32_hrp_expand(hrp) + data)
if check == BECH32_CONST:
return Encoding.BECH32
elif check == BECH32M_CONST:
return Encoding.BECH32M
else:
return None
def bech32_create_checksum(hrp, data):
def bech32_create_checksum(encoding: Encoding, hrp: str, data: List[int]) -> List[int]:
"""Compute the checksum values given HRP and data."""
values = bech32_hrp_expand(hrp) + data
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
const = BECH32M_CONST if encoding == Encoding.BECH32M else BECH32_CONST
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ const
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
def bech32_encode(hrp, data):
"""Compute a Bech32 string given HRP and data values."""
combined = data + bech32_create_checksum(hrp, data)
def bech32_encode(encoding: Encoding, hrp: str, data: List[int]) -> str:
"""Compute a Bech32 or Bech32m string given HRP and data values."""
combined = data + bech32_create_checksum(encoding, hrp, data)
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
def bech32_decode(bech: str, ignore_long_length=False):
"""Validate a Bech32 string, and determine HRP and data."""
def bech32_decode(bech: str, *, ignore_long_length=False) -> DecodedBech32:
"""Validate a Bech32/Bech32m string, and determine HRP and data."""
bech_lower = bech.lower()
if bech_lower != bech and bech.upper() != bech:
return (None, None)
return DecodedBech32(None, None, None)
pos = bech.rfind('1')
if pos < 1 or pos + 7 > len(bech) or (not ignore_long_length and len(bech) > 90):
return (None, None)
return DecodedBech32(None, None, None)
# check that HRP only consists of sane ASCII chars
if any(ord(x) < 33 or ord(x) > 126 for x in bech[:pos+1]):
return (None, None)
return DecodedBech32(None, None, None)
bech = bech_lower
hrp = bech[:pos]
try:
data = [_CHARSET_INVERSE[x] for x in bech[pos+1:]]
except KeyError:
return (None, None)
if not bech32_verify_checksum(hrp, data):
return (None, None)
return (hrp, data[:-6])
return DecodedBech32(None, None, None)
encoding = bech32_verify_checksum(hrp, data)
if encoding is None:
return DecodedBech32(None, None, None)
return DecodedBech32(encoding=encoding, hrp=hrp, data=data[:-6])
def convertbits(data, frombits, tobits, pad=True):
@ -106,11 +131,11 @@ def convertbits(data, frombits, tobits, pad=True):
return ret
def decode(hrp, addr):
def decode_segwit_address(hrp: str, addr: Optional[str]) -> Tuple[Optional[int], Optional[Sequence[int]]]:
"""Decode a segwit address."""
if addr is None:
return (None, None)
hrpgot, data = bech32_decode(addr)
encoding, hrpgot, data = bech32_decode(addr)
if hrpgot != hrp:
return (None, None)
decoded = convertbits(data[1:], 5, 8, False)
@ -120,11 +145,15 @@ def decode(hrp, addr):
return (None, None)
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
return (None, None)
if (data[0] == 0 and encoding != Encoding.BECH32) or (data[0] != 0 and encoding != Encoding.BECH32M):
return (None, None)
return (data[0], decoded)
def encode(hrp, witver, witprog):
def encode_segwit_address(hrp: str, witver: int, witprog: bytes) -> Optional[str]:
"""Encode a segwit address."""
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
assert decode(hrp, ret) != (None, None)
encoding = Encoding.BECH32 if witver == 0 else Encoding.BECH32M
ret = bech32_encode(encoding, hrp, [witver] + convertbits(witprog, 8, 5))
if decode_segwit_address(hrp, ret) == (None, None):
return None
return ret

144
electrum/tests/test_bitcoin.py

@ -11,6 +11,7 @@ from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key,
opcodes, base_encode, base_decode, BitcoinException)
from electrum import bip32
from electrum import segwit_addr
from electrum.segwit_addr import DecodedBech32
from electrum.bip32 import (BIP32Node, convert_bip32_intpath_to_strpath,
xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation,
is_xpub, convert_bip32_path_to_list_of_uint32,
@ -426,12 +427,20 @@ class Test_bitcoin(ElectrumTestCase):
self.assertEqual(add_number_to_script(2147483647), bfh('04ffffff7f'))
def test_address_to_script(self):
# bech32 native segwit
# bech32/bech32m native segwit
# test vectors from BIP-0173
# note: the ones that are commented out have been invalidated by BIP-0350
self.assertEqual(address_to_script('BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4'), '0014751e76e8199196d454941c45d1b3a323f1433bd6')
self.assertEqual(address_to_script('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'), '5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6')
self.assertEqual(address_to_script('BC1SW50QA3JX3S'), '6002751e')
self.assertEqual(address_to_script('bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj'), '5210751e76e8199196d454941c45d1b3a323')
# self.assertEqual(address_to_script('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'), '5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6')
# self.assertEqual(address_to_script('BC1SW50QA3JX3S'), '6002751e')
# self.assertEqual(address_to_script('bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj'), '5210751e76e8199196d454941c45d1b3a323')
# bech32/bech32m native segwit
# test vectors from BIP-0350
self.assertEqual(address_to_script('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y'), '5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6')
self.assertEqual(address_to_script('BC1SW50QGDZ25J'), '6002751e')
self.assertEqual(address_to_script('bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs'), '5210751e76e8199196d454941c45d1b3a323')
self.assertEqual(address_to_script('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0'), '512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
# invalid addresses (from BIP-0173)
self.assertFalse(is_address('tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty'))
@ -445,6 +454,23 @@ class Test_bitcoin(ElectrumTestCase):
self.assertFalse(is_address('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv'))
self.assertFalse(is_address('bc1gmk9yu'))
# invalid addresses (from BIP-0350)
self.assertFalse(is_address('tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut'))
self.assertFalse(is_address('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd'))
self.assertFalse(is_address('tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf'))
self.assertFalse(is_address('BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL'))
self.assertFalse(is_address('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh'))
self.assertFalse(is_address('tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47'))
self.assertFalse(is_address('bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4'))
self.assertFalse(is_address('BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R'))
self.assertFalse(is_address('bc1pw5dgrnzv'))
self.assertFalse(is_address('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav'))
self.assertFalse(is_address('BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P'))
self.assertFalse(is_address('tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq'))
self.assertFalse(is_address('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf'))
self.assertFalse(is_address('tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j'))
self.assertFalse(is_address('bc1gmk9yu'))
# base58 P2PKH
self.assertEqual(address_to_script('14gcRovpkCoGkCNBivQBvw7eso7eiNAbxG'), '76a91428662c67561b95c79d2257d2a93d9d151c977e9188ac')
self.assertEqual(address_to_script('1BEqfzh4Y3zzLosfGhw1AsqbEKVW6e1qHv'), '76a914704f4b81cadb7bf7e68c08cd3657220f680f863c88ac')
@ -453,58 +479,109 @@ class Test_bitcoin(ElectrumTestCase):
self.assertEqual(address_to_script('35ZqQJcBQMZ1rsv8aSuJ2wkC7ohUCQMJbT'), 'a9142a84cf00d47f699ee7bbc1dea5ec1bdecb4ac15487')
self.assertEqual(address_to_script('3PyjzJ3im7f7bcV724GR57edKDqoZvH7Ji'), 'a914f47c8954e421031ad04ecd8e7752c9479206b9d387')
def test_bech32_decode(self):
# bech32 native segwit
# test vectors from BIP-0173
self.assertEqual(('a', []),
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, 'a', []),
segwit_addr.bech32_decode('A12UEL5L'))
self.assertEqual(('a', []),
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, 'a', []),
segwit_addr.bech32_decode('a12uel5l'))
self.assertEqual(('an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio', []),
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, 'an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio', []),
segwit_addr.bech32_decode('an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs'))
self.assertEqual(('abcdef', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]),
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, 'abcdef', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]),
segwit_addr.bech32_decode('abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw'))
self.assertEqual(('1', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, '1', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
segwit_addr.bech32_decode('11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j'))
self.assertEqual(('split', [24, 23, 25, 24, 22, 28, 1, 16, 11, 29, 8, 25, 23, 29, 19, 13, 16, 23, 29, 22, 25, 28, 1, 16, 11, 3, 25, 29, 27, 25, 3, 3, 29, 19, 11, 25, 3, 3, 25, 13, 24, 29, 1, 25, 3, 3, 25, 13]),
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, 'split', [24, 23, 25, 24, 22, 28, 1, 16, 11, 29, 8, 25, 23, 29, 19, 13, 16, 23, 29, 22, 25, 28, 1, 16, 11, 3, 25, 29, 27, 25, 3, 3, 29, 19, 11, 25, 3, 3, 25, 13, 24, 29, 1, 25, 3, 3, 25, 13]),
segwit_addr.bech32_decode('split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w'))
self.assertEqual(('?', []),
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, '?', []),
segwit_addr.bech32_decode('?1ezyfcl'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('\x201nwldj5'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('\x7f1axkwrx'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('\x801eym55h'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('pzry9x0s0muk'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('1pzry9x0s0muk'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('x1b4n0q5v'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('li1dgmt3'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('de1lg7wt\xff'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('A1G7SGD8'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('10a06t8'))
self.assertEqual((None, None),
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('1qzzfhee'))
# test vectors from BIP-0350
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32M, 'a', []),
segwit_addr.bech32_decode('A1LQFN3A'))
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32M, 'a', []),
segwit_addr.bech32_decode('a1lqfn3a'))
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32M, 'an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber1', []),
segwit_addr.bech32_decode('an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6'))
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32M, 'abcdef', [31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]),
segwit_addr.bech32_decode('abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx'))
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32M, '1', [31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31]),
segwit_addr.bech32_decode('11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8'))
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32M, 'split', [24, 23, 25, 24, 22, 28, 1, 16, 11, 29, 8, 25, 23, 29, 19, 13, 16, 23, 29, 22, 25, 28, 1, 16, 11, 3, 25, 29, 27, 25, 3, 3, 29, 19, 11, 25, 3, 3, 25, 13, 24, 29, 1, 25, 3, 3, 25, 13]),
segwit_addr.bech32_decode('split1checkupstagehandshakeupstreamerranterredcaperredlc445v'))
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32M, '?', []),
segwit_addr.bech32_decode('?1v759aa'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('\x201xj0phk'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('\x7f1g6xzxy'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('\x801vctc34'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('qyrz8wqd2c9m'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('1qyrz8wqd2c9m'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('y1b0jsk6g'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('lt1igcx5c0'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('in1muywd'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('mm1crxm3i'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('au1s5cgom'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('M1VUXWEZ'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('16plkw9'))
self.assertEqual(DecodedBech32(None, None, None),
segwit_addr.bech32_decode('1p2gdwpf'))
class Test_bitcoin_testnet(TestCaseForTestnet):
def test_address_to_script(self):
# bech32 native segwit
# bech32/bech32m native segwit
# test vectors from BIP-0173
self.assertEqual(address_to_script('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7'), '00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262')
self.assertEqual(address_to_script('tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy'), '0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433')
# bech32/bech32m native segwit
# test vectors from BIP-0350
self.assertEqual(address_to_script('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7'), '00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262')
self.assertEqual(address_to_script('tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy'), '0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433')
self.assertEqual(address_to_script('tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c'), '5120000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433')
# invalid addresses (from BIP-0173)
self.assertFalse(is_address('tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty'))
self.assertFalse(is_address('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5'))
@ -517,6 +594,23 @@ class Test_bitcoin_testnet(TestCaseForTestnet):
self.assertFalse(is_address('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv'))
self.assertFalse(is_address('bc1gmk9yu'))
# invalid addresses (from BIP-0350)
self.assertFalse(is_address('tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut'))
self.assertFalse(is_address('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd'))
self.assertFalse(is_address('tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf'))
self.assertFalse(is_address('BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL'))
self.assertFalse(is_address('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh'))
self.assertFalse(is_address('tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47'))
self.assertFalse(is_address('bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4'))
self.assertFalse(is_address('BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R'))
self.assertFalse(is_address('bc1pw5dgrnzv'))
self.assertFalse(is_address('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav'))
self.assertFalse(is_address('BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P'))
self.assertFalse(is_address('tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq'))
self.assertFalse(is_address('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf'))
self.assertFalse(is_address('tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j'))
self.assertFalse(is_address('bc1gmk9yu'))
# base58 P2PKH
self.assertEqual(address_to_script('mutXcGt1CJdkRvXuN2xoz2quAAQYQ59bRX'), '76a9149da64e300c5e4eb4aaffc9c2fd465348d5618ad488ac')
self.assertEqual(address_to_script('miqtaRTkU3U8rzwKbEHx3g8FSz8GJtPS3K'), '76a914247d2d5b6334bdfa2038e85b20fc15264f8e5d2788ac')

16
electrum/tests/test_bolt11.py

@ -6,6 +6,7 @@ import unittest
from electrum.lnaddr import shorten_amount, unshorten_amount, LnAddr, lnencode, lndecode, u5_to_bitarray, bitarray_to_u5
from electrum.segwit_addr import bech32_encode, bech32_decode
from electrum import segwit_addr
from electrum.lnutil import UnknownEvenFeatureBits, derive_payment_secret_from_payment_preimage, LnFeatures
from . import ElectrumTestCase
@ -114,20 +115,21 @@ class TestBolt11(ElectrumTestCase):
def test_n_decoding(self):
# We flip the signature recovery bit, which would normally give a different
# pubkey.
hrp, data = bech32_decode(lnencode(LnAddr(paymenthash=RHASH, amount=24, tags=[('d', '')]), PRIVKEY), True)
_, hrp, data = bech32_decode(
lnencode(LnAddr(paymenthash=RHASH, amount=24, tags=[('d', '')]), PRIVKEY),
ignore_long_length=True)
databits = u5_to_bitarray(data)
databits.invert(-1)
lnaddr = lndecode(bech32_encode(hrp, bitarray_to_u5(databits)), verbose=True)
lnaddr = lndecode(bech32_encode(segwit_addr.Encoding.BECH32, hrp, bitarray_to_u5(databits)), verbose=True)
assert lnaddr.pubkey.serialize() != PUBKEY
# But not if we supply expliciy `n` specifier!
hrp, data = bech32_decode(lnencode(LnAddr(paymenthash=RHASH, amount=24,
tags=[('d', ''),
('n', PUBKEY)]),
PRIVKEY), True)
_, hrp, data = bech32_decode(
lnencode(LnAddr(paymenthash=RHASH, amount=24, tags=[('d', ''), ('n', PUBKEY)]), PRIVKEY),
ignore_long_length=True)
databits = u5_to_bitarray(data)
databits.invert(-1)
lnaddr = lndecode(bech32_encode(hrp, bitarray_to_u5(databits)), verbose=True)
lnaddr = lndecode(bech32_encode(segwit_addr.Encoding.BECH32, hrp, bitarray_to_u5(databits)), verbose=True)
assert lnaddr.pubkey.serialize() == PUBKEY
def test_min_final_cltv_expiry_decoding(self):

11
electrum/tests/test_transaction.py

@ -158,12 +158,13 @@ class TestTransaction(ElectrumTestCase):
# the inverse of this test is in test_bitcoin: test_address_to_script
addr_from_script = lambda script: transaction.get_address_from_output_script(bfh(script))
# bech32 native segwit
# test vectors from BIP-0173
# bech32/bech32m native segwit
# test vectors from BIP-0173/BIP-0350
self.assertEqual('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', addr_from_script('0014751e76e8199196d454941c45d1b3a323f1433bd6'))
self.assertEqual('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx', addr_from_script('5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6'))
self.assertEqual('bc1sw50qa3jx3s', addr_from_script('6002751e'))
self.assertEqual('bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj', addr_from_script('5210751e76e8199196d454941c45d1b3a323'))
self.assertEqual('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y', addr_from_script('5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6'))
self.assertEqual('bc1sw50qgdz25j', addr_from_script('6002751e'))
self.assertEqual('bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs', addr_from_script('5210751e76e8199196d454941c45d1b3a323'))
self.assertEqual('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0', addr_from_script('512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'))
# almost but not quite
self.assertEqual(None, addr_from_script('0013751e76e8199196d454941c45d1b3a323f1433b'))

2
electrum/transaction.py

@ -687,7 +687,7 @@ class Transaction:
# the estimation will not be precise.
if addr is None:
return 'p2wpkh'
witver, witprog = segwit_addr.decode(constants.net.SEGWIT_HRP, addr)
witver, witprog = segwit_addr.decode_segwit_address(constants.net.SEGWIT_HRP, addr)
if witprog is not None:
return 'p2wpkh'
addrtype, hash_160_ = b58_address_to_hash160(addr)

Loading…
Cancel
Save