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:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
#
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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'))
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user