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:
|
def hash_to_segwit_addr(h: bytes, witver: int, *, net=None) -> str:
|
||||||
if net is None: net = constants.net
|
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:
|
def public_key_to_p2wpkh(public_key: bytes, *, net=None) -> str:
|
||||||
if net is None: net = constants.net
|
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 net is None: net = constants.net
|
||||||
if not is_address(addr, net=net):
|
if not is_address(addr, net=net):
|
||||||
raise BitcoinException(f"invalid bitcoin address: {addr}")
|
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 witprog is not None:
|
||||||
if not (0 <= witver <= 16):
|
if not (0 <= witver <= 16):
|
||||||
raise BitcoinException(f'impossible witness version: {witver}')
|
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 net is None: net = constants.net
|
||||||
if not is_address(addr, net=net):
|
if not is_address(addr, net=net):
|
||||||
raise BitcoinException(f"invalid bitcoin address: {addr}")
|
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 witprog is not None:
|
||||||
if witver != 0:
|
if witver != 0:
|
||||||
raise BitcoinException(f"not implemented handling for witver={witver}")
|
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:
|
def is_segwit_address(addr: str, *, net=None) -> bool:
|
||||||
if net is None: net = constants.net
|
if net is None: net = constants.net
|
||||||
try:
|
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:
|
except Exception as e:
|
||||||
return False
|
return False
|
||||||
return witprog is not None
|
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 .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 .segwit_addr import bech32_encode, bech32_decode, CHARSET
|
||||||
|
from . import segwit_addr
|
||||||
from . import constants
|
from . import constants
|
||||||
from . import ecc
|
from . import ecc
|
||||||
from .bitcoin import COIN
|
from .bitcoin import COIN
|
||||||
@@ -81,18 +82,14 @@ def bitarray_to_u5(barr):
|
|||||||
ret.append(s.read(5).uint)
|
ret.append(s.read(5).uint)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def encode_fallback(fallback, currency):
|
|
||||||
|
def encode_fallback(fallback: str, currency):
|
||||||
""" Encode all supported fallback addresses.
|
""" Encode all supported fallback addresses.
|
||||||
"""
|
"""
|
||||||
if currency in [constants.BitcoinMainnet.SEGWIT_HRP, constants.BitcoinTestnet.SEGWIT_HRP]:
|
if currency in [constants.BitcoinMainnet.SEGWIT_HRP, constants.BitcoinTestnet.SEGWIT_HRP]:
|
||||||
fbhrp, witness = bech32_decode(fallback, ignore_long_length=True)
|
wver, wprog_ints = segwit_addr.decode_segwit_address(currency, fallback)
|
||||||
if fbhrp:
|
if wver is not None:
|
||||||
if fbhrp != currency:
|
wprog = bytes(wprog_ints)
|
||||||
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:])
|
|
||||||
else:
|
else:
|
||||||
addrtype, addr = b58_address_to_hash160(fallback)
|
addrtype, addr = b58_address_to_hash160(fallback)
|
||||||
if is_p2pkh(currency, addrtype):
|
if is_p2pkh(currency, addrtype):
|
||||||
@@ -106,6 +103,7 @@ def encode_fallback(fallback, currency):
|
|||||||
else:
|
else:
|
||||||
raise NotImplementedError("Support for currency {} not implemented".format(currency))
|
raise NotImplementedError("Support for currency {} not implemented".format(currency))
|
||||||
|
|
||||||
|
|
||||||
def parse_fallback(fallback, currency):
|
def parse_fallback(fallback, currency):
|
||||||
if currency in [constants.BitcoinMainnet.SEGWIT_HRP, constants.BitcoinTestnet.SEGWIT_HRP]:
|
if currency in [constants.BitcoinMainnet.SEGWIT_HRP, constants.BitcoinTestnet.SEGWIT_HRP]:
|
||||||
wver = fallback[0:5].uint
|
wver = fallback[0:5].uint
|
||||||
@@ -114,7 +112,10 @@ def parse_fallback(fallback, currency):
|
|||||||
elif wver == 18:
|
elif wver == 18:
|
||||||
addr=hash160_to_b58_address(fallback[5:].tobytes(), base58_prefix_map[currency][1])
|
addr=hash160_to_b58_address(fallback[5:].tobytes(), base58_prefix_map[currency][1])
|
||||||
elif wver <= 16:
|
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:
|
else:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -262,7 +263,7 @@ def lnencode(addr: 'LnAddr', privkey) -> str:
|
|||||||
sig = bytes(sig[1:]) + recovery_flag
|
sig = bytes(sig[1:]) + recovery_flag
|
||||||
data += sig
|
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):
|
class LnAddr(object):
|
||||||
def __init__(self, *, paymenthash: bytes = None, amount=None, currency=None, tags=None, date=None,
|
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:
|
def lndecode(invoice: str, *, verbose=False, expected_hrp=None) -> LnAddr:
|
||||||
if expected_hrp is None:
|
if expected_hrp is None:
|
||||||
expected_hrp = constants.net.SEGWIT_HRP
|
expected_hrp = constants.net.SEGWIT_HRP
|
||||||
hrp, data = bech32_decode(invoice, ignore_long_length=True)
|
decoded_bech32 = bech32_decode(invoice, ignore_long_length=True)
|
||||||
if not hrp:
|
hrp = decoded_bech32.hrp
|
||||||
|
data = decoded_bech32.data
|
||||||
|
if decoded_bech32.encoding is None:
|
||||||
raise ValueError("Bad bech32 checksum")
|
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:
|
# BOLT #11:
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1165,7 +1165,13 @@ class LNPeerAddr:
|
|||||||
|
|
||||||
|
|
||||||
def get_compressed_pubkey_from_bech32(bech32_pubkey: str) -> bytes:
|
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':
|
if hrp != 'ln':
|
||||||
raise Exception('unexpected hrp: {}'.format(hrp))
|
raise Exception('unexpected hrp: {}'.format(hrp))
|
||||||
data_8bits = segwit_addr.convertbits(data_5bits, 5, 8, False)
|
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
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
# THE SOFTWARE.
|
# 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 = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
||||||
_CHARSET_INVERSE = {x: CHARSET.find(x) for x in CHARSET}
|
_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):
|
def bech32_polymod(values):
|
||||||
"""Internal function that computes the Bech32 checksum."""
|
"""Internal function that computes the Bech32 checksum."""
|
||||||
@@ -45,42 +62,50 @@ def bech32_hrp_expand(hrp):
|
|||||||
|
|
||||||
def bech32_verify_checksum(hrp, data):
|
def bech32_verify_checksum(hrp, data):
|
||||||
"""Verify a checksum given HRP and converted data characters."""
|
"""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."""
|
"""Compute the checksum values given HRP and data."""
|
||||||
values = bech32_hrp_expand(hrp) + 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)]
|
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
|
||||||
|
|
||||||
|
|
||||||
def bech32_encode(hrp, data):
|
def bech32_encode(encoding: Encoding, hrp: str, data: List[int]) -> str:
|
||||||
"""Compute a Bech32 string given HRP and data values."""
|
"""Compute a Bech32 or Bech32m string given HRP and data values."""
|
||||||
combined = data + bech32_create_checksum(hrp, data)
|
combined = data + bech32_create_checksum(encoding, hrp, data)
|
||||||
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
|
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
|
||||||
|
|
||||||
|
|
||||||
def bech32_decode(bech: str, ignore_long_length=False):
|
def bech32_decode(bech: str, *, ignore_long_length=False) -> DecodedBech32:
|
||||||
"""Validate a Bech32 string, and determine HRP and data."""
|
"""Validate a Bech32/Bech32m string, and determine HRP and data."""
|
||||||
bech_lower = bech.lower()
|
bech_lower = bech.lower()
|
||||||
if bech_lower != bech and bech.upper() != bech:
|
if bech_lower != bech and bech.upper() != bech:
|
||||||
return (None, None)
|
return DecodedBech32(None, None, None)
|
||||||
pos = bech.rfind('1')
|
pos = bech.rfind('1')
|
||||||
if pos < 1 or pos + 7 > len(bech) or (not ignore_long_length and len(bech) > 90):
|
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
|
# check that HRP only consists of sane ASCII chars
|
||||||
if any(ord(x) < 33 or ord(x) > 126 for x in bech[:pos+1]):
|
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
|
bech = bech_lower
|
||||||
hrp = bech[:pos]
|
hrp = bech[:pos]
|
||||||
try:
|
try:
|
||||||
data = [_CHARSET_INVERSE[x] for x in bech[pos+1:]]
|
data = [_CHARSET_INVERSE[x] for x in bech[pos+1:]]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return (None, None)
|
return DecodedBech32(None, None, None)
|
||||||
if not bech32_verify_checksum(hrp, data):
|
encoding = bech32_verify_checksum(hrp, data)
|
||||||
return (None, None)
|
if encoding is None:
|
||||||
return (hrp, data[:-6])
|
return DecodedBech32(None, None, None)
|
||||||
|
return DecodedBech32(encoding=encoding, hrp=hrp, data=data[:-6])
|
||||||
|
|
||||||
|
|
||||||
def convertbits(data, frombits, tobits, pad=True):
|
def convertbits(data, frombits, tobits, pad=True):
|
||||||
@@ -106,11 +131,11 @@ def convertbits(data, frombits, tobits, pad=True):
|
|||||||
return ret
|
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."""
|
"""Decode a segwit address."""
|
||||||
if addr is None:
|
if addr is None:
|
||||||
return (None, None)
|
return (None, None)
|
||||||
hrpgot, data = bech32_decode(addr)
|
encoding, hrpgot, data = bech32_decode(addr)
|
||||||
if hrpgot != hrp:
|
if hrpgot != hrp:
|
||||||
return (None, None)
|
return (None, None)
|
||||||
decoded = convertbits(data[1:], 5, 8, False)
|
decoded = convertbits(data[1:], 5, 8, False)
|
||||||
@@ -120,11 +145,15 @@ def decode(hrp, addr):
|
|||||||
return (None, None)
|
return (None, None)
|
||||||
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
|
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
|
||||||
return (None, None)
|
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)
|
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."""
|
"""Encode a segwit address."""
|
||||||
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
|
encoding = Encoding.BECH32 if witver == 0 else Encoding.BECH32M
|
||||||
assert decode(hrp, ret) != (None, None)
|
ret = bech32_encode(encoding, hrp, [witver] + convertbits(witprog, 8, 5))
|
||||||
|
if decode_segwit_address(hrp, ret) == (None, None):
|
||||||
|
return None
|
||||||
return ret
|
return ret
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key,
|
|||||||
opcodes, base_encode, base_decode, BitcoinException)
|
opcodes, base_encode, base_decode, BitcoinException)
|
||||||
from electrum import bip32
|
from electrum import bip32
|
||||||
from electrum import segwit_addr
|
from electrum import segwit_addr
|
||||||
|
from electrum.segwit_addr import DecodedBech32
|
||||||
from electrum.bip32 import (BIP32Node, convert_bip32_intpath_to_strpath,
|
from electrum.bip32 import (BIP32Node, convert_bip32_intpath_to_strpath,
|
||||||
xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation,
|
xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation,
|
||||||
is_xpub, convert_bip32_path_to_list_of_uint32,
|
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'))
|
self.assertEqual(add_number_to_script(2147483647), bfh('04ffffff7f'))
|
||||||
|
|
||||||
def test_address_to_script(self):
|
def test_address_to_script(self):
|
||||||
# bech32 native segwit
|
# bech32/bech32m native segwit
|
||||||
# test vectors from BIP-0173
|
# 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('BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4'), '0014751e76e8199196d454941c45d1b3a323f1433bd6')
|
||||||
self.assertEqual(address_to_script('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'), '5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6')
|
# self.assertEqual(address_to_script('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'), '5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6')
|
||||||
self.assertEqual(address_to_script('BC1SW50QA3JX3S'), '6002751e')
|
# self.assertEqual(address_to_script('BC1SW50QA3JX3S'), '6002751e')
|
||||||
self.assertEqual(address_to_script('bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj'), '5210751e76e8199196d454941c45d1b3a323')
|
# 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)
|
# invalid addresses (from BIP-0173)
|
||||||
self.assertFalse(is_address('tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty'))
|
self.assertFalse(is_address('tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty'))
|
||||||
@@ -445,6 +454,23 @@ class Test_bitcoin(ElectrumTestCase):
|
|||||||
self.assertFalse(is_address('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv'))
|
self.assertFalse(is_address('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv'))
|
||||||
self.assertFalse(is_address('bc1gmk9yu'))
|
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
|
# base58 P2PKH
|
||||||
self.assertEqual(address_to_script('14gcRovpkCoGkCNBivQBvw7eso7eiNAbxG'), '76a91428662c67561b95c79d2257d2a93d9d151c977e9188ac')
|
self.assertEqual(address_to_script('14gcRovpkCoGkCNBivQBvw7eso7eiNAbxG'), '76a91428662c67561b95c79d2257d2a93d9d151c977e9188ac')
|
||||||
self.assertEqual(address_to_script('1BEqfzh4Y3zzLosfGhw1AsqbEKVW6e1qHv'), '76a914704f4b81cadb7bf7e68c08cd3657220f680f863c88ac')
|
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('35ZqQJcBQMZ1rsv8aSuJ2wkC7ohUCQMJbT'), 'a9142a84cf00d47f699ee7bbc1dea5ec1bdecb4ac15487')
|
||||||
self.assertEqual(address_to_script('3PyjzJ3im7f7bcV724GR57edKDqoZvH7Ji'), 'a914f47c8954e421031ad04ecd8e7752c9479206b9d387')
|
self.assertEqual(address_to_script('3PyjzJ3im7f7bcV724GR57edKDqoZvH7Ji'), 'a914f47c8954e421031ad04ecd8e7752c9479206b9d387')
|
||||||
|
|
||||||
|
|
||||||
def test_bech32_decode(self):
|
def test_bech32_decode(self):
|
||||||
# bech32 native segwit
|
# bech32 native segwit
|
||||||
# test vectors from BIP-0173
|
# test vectors from BIP-0173
|
||||||
self.assertEqual(('a', []),
|
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, 'a', []),
|
||||||
segwit_addr.bech32_decode('A12UEL5L'))
|
segwit_addr.bech32_decode('A12UEL5L'))
|
||||||
self.assertEqual(('a', []),
|
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, 'a', []),
|
||||||
segwit_addr.bech32_decode('a12uel5l'))
|
segwit_addr.bech32_decode('a12uel5l'))
|
||||||
self.assertEqual(('an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio', []),
|
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, 'an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio', []),
|
||||||
segwit_addr.bech32_decode('an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs'))
|
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'))
|
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'))
|
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'))
|
segwit_addr.bech32_decode('split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w'))
|
||||||
self.assertEqual(('?', []),
|
self.assertEqual(DecodedBech32(segwit_addr.Encoding.BECH32, '?', []),
|
||||||
segwit_addr.bech32_decode('?1ezyfcl'))
|
segwit_addr.bech32_decode('?1ezyfcl'))
|
||||||
|
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('\x201nwldj5'))
|
segwit_addr.bech32_decode('\x201nwldj5'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('\x7f1axkwrx'))
|
segwit_addr.bech32_decode('\x7f1axkwrx'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('\x801eym55h'))
|
segwit_addr.bech32_decode('\x801eym55h'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx'))
|
segwit_addr.bech32_decode('an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('pzry9x0s0muk'))
|
segwit_addr.bech32_decode('pzry9x0s0muk'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('1pzry9x0s0muk'))
|
segwit_addr.bech32_decode('1pzry9x0s0muk'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('x1b4n0q5v'))
|
segwit_addr.bech32_decode('x1b4n0q5v'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('li1dgmt3'))
|
segwit_addr.bech32_decode('li1dgmt3'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('de1lg7wt\xff'))
|
segwit_addr.bech32_decode('de1lg7wt\xff'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('A1G7SGD8'))
|
segwit_addr.bech32_decode('A1G7SGD8'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('10a06t8'))
|
segwit_addr.bech32_decode('10a06t8'))
|
||||||
self.assertEqual((None, None),
|
self.assertEqual(DecodedBech32(None, None, None),
|
||||||
segwit_addr.bech32_decode('1qzzfhee'))
|
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):
|
class Test_bitcoin_testnet(TestCaseForTestnet):
|
||||||
|
|
||||||
def test_address_to_script(self):
|
def test_address_to_script(self):
|
||||||
# bech32 native segwit
|
# bech32/bech32m native segwit
|
||||||
# test vectors from BIP-0173
|
# test vectors from BIP-0173
|
||||||
self.assertEqual(address_to_script('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7'), '00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262')
|
self.assertEqual(address_to_script('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7'), '00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262')
|
||||||
self.assertEqual(address_to_script('tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy'), '0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433')
|
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)
|
# invalid addresses (from BIP-0173)
|
||||||
self.assertFalse(is_address('tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty'))
|
self.assertFalse(is_address('tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty'))
|
||||||
self.assertFalse(is_address('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5'))
|
self.assertFalse(is_address('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5'))
|
||||||
@@ -517,6 +594,23 @@ class Test_bitcoin_testnet(TestCaseForTestnet):
|
|||||||
self.assertFalse(is_address('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv'))
|
self.assertFalse(is_address('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv'))
|
||||||
self.assertFalse(is_address('bc1gmk9yu'))
|
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
|
# base58 P2PKH
|
||||||
self.assertEqual(address_to_script('mutXcGt1CJdkRvXuN2xoz2quAAQYQ59bRX'), '76a9149da64e300c5e4eb4aaffc9c2fd465348d5618ad488ac')
|
self.assertEqual(address_to_script('mutXcGt1CJdkRvXuN2xoz2quAAQYQ59bRX'), '76a9149da64e300c5e4eb4aaffc9c2fd465348d5618ad488ac')
|
||||||
self.assertEqual(address_to_script('miqtaRTkU3U8rzwKbEHx3g8FSz8GJtPS3K'), '76a914247d2d5b6334bdfa2038e85b20fc15264f8e5d2788ac')
|
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.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.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 electrum.lnutil import UnknownEvenFeatureBits, derive_payment_secret_from_payment_preimage, LnFeatures
|
||||||
|
|
||||||
from . import ElectrumTestCase
|
from . import ElectrumTestCase
|
||||||
@@ -114,20 +115,21 @@ class TestBolt11(ElectrumTestCase):
|
|||||||
def test_n_decoding(self):
|
def test_n_decoding(self):
|
||||||
# We flip the signature recovery bit, which would normally give a different
|
# We flip the signature recovery bit, which would normally give a different
|
||||||
# pubkey.
|
# 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 = u5_to_bitarray(data)
|
||||||
databits.invert(-1)
|
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
|
assert lnaddr.pubkey.serialize() != PUBKEY
|
||||||
|
|
||||||
# But not if we supply expliciy `n` specifier!
|
# But not if we supply expliciy `n` specifier!
|
||||||
hrp, data = bech32_decode(lnencode(LnAddr(paymenthash=RHASH, amount=24,
|
_, hrp, data = bech32_decode(
|
||||||
tags=[('d', ''),
|
lnencode(LnAddr(paymenthash=RHASH, amount=24, tags=[('d', ''), ('n', PUBKEY)]), PRIVKEY),
|
||||||
('n', PUBKEY)]),
|
ignore_long_length=True)
|
||||||
PRIVKEY), True)
|
|
||||||
databits = u5_to_bitarray(data)
|
databits = u5_to_bitarray(data)
|
||||||
databits.invert(-1)
|
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
|
assert lnaddr.pubkey.serialize() == PUBKEY
|
||||||
|
|
||||||
def test_min_final_cltv_expiry_decoding(self):
|
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
|
# 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))
|
addr_from_script = lambda script: transaction.get_address_from_output_script(bfh(script))
|
||||||
|
|
||||||
# bech32 native segwit
|
# bech32/bech32m native segwit
|
||||||
# test vectors from BIP-0173
|
# test vectors from BIP-0173/BIP-0350
|
||||||
self.assertEqual('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', addr_from_script('0014751e76e8199196d454941c45d1b3a323f1433bd6'))
|
self.assertEqual('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', addr_from_script('0014751e76e8199196d454941c45d1b3a323f1433bd6'))
|
||||||
self.assertEqual('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx', addr_from_script('5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6'))
|
self.assertEqual('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y', addr_from_script('5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6'))
|
||||||
self.assertEqual('bc1sw50qa3jx3s', addr_from_script('6002751e'))
|
self.assertEqual('bc1sw50qgdz25j', addr_from_script('6002751e'))
|
||||||
self.assertEqual('bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj', addr_from_script('5210751e76e8199196d454941c45d1b3a323'))
|
self.assertEqual('bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs', addr_from_script('5210751e76e8199196d454941c45d1b3a323'))
|
||||||
|
self.assertEqual('bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0', addr_from_script('512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'))
|
||||||
# almost but not quite
|
# almost but not quite
|
||||||
self.assertEqual(None, addr_from_script('0013751e76e8199196d454941c45d1b3a323f1433b'))
|
self.assertEqual(None, addr_from_script('0013751e76e8199196d454941c45d1b3a323f1433b'))
|
||||||
|
|
||||||
|
|||||||
@@ -687,7 +687,7 @@ class Transaction:
|
|||||||
# the estimation will not be precise.
|
# the estimation will not be precise.
|
||||||
if addr is None:
|
if addr is None:
|
||||||
return 'p2wpkh'
|
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:
|
if witprog is not None:
|
||||||
return 'p2wpkh'
|
return 'p2wpkh'
|
||||||
addrtype, hash_160_ = b58_address_to_hash160(addr)
|
addrtype, hash_160_ = b58_address_to_hash160(addr)
|
||||||
|
|||||||
Reference in New Issue
Block a user