diff --git a/tests/test_btc_address.py b/tests/test_btc_address.py index 68d09f67..5ba1fc0d 100644 --- a/tests/test_btc_address.py +++ b/tests/test_btc_address.py @@ -1,35 +1,41 @@ +"""Test BTC address.""" # -*- coding: utf-8 -*- + +# standard import pytest +# local from validators import btc_address, ValidationFailure @pytest.mark.parametrize( - 'value', + "value", [ # P2PKH (Pay-to-PubkeyHash) type - '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2', + "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", # P2SH (Pay to script hash) type - '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy', + "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", # Bech32/segwit type - 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', - 'bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9', + "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", + "bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9", ], ) -def test_returns_true_on_valid_btc_address(value): +def test_returns_true_on_valid_btc_address(value: str): + """Test returns true on valid btc address.""" assert btc_address(value) @pytest.mark.parametrize( - 'value', + "value", [ - 'ff3Cwgr2g7vsi1bXDUkpEnVoRLA9w4FZfC69', - 'b3Cgwgr2g7vsi1bXyjyDUkphEnVoRLA9w4FZfC69', + "ff3Cwgr2g7vsi1bXDUkpEnVoRLA9w4FZfC69", + "b3Cgwgr2g7vsi1bXyjyDUkphEnVoRLA9w4FZfC69", # incorrect header - '1BvBMsEYstWetqTFn5Au4m4GFg7xJaNVN2', + "1BvBMsEYstWetqTFn5Au4m4GFg7xJaNVN2", # incorrect checksum - '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLz', + "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLz", ], ) -def test_returns_failed_validation_on_invalid_btc_address(value): +def test_returns_failed_validation_on_invalid_btc_address(value: str): + """Test returns failed validation on invalid btc address.""" assert isinstance(btc_address(value), ValidationFailure) diff --git a/validators/btc_address.py b/validators/btc_address.py index 35ada853..a4984c54 100644 --- a/validators/btc_address.py +++ b/validators/btc_address.py @@ -1,55 +1,61 @@ -import re +"""BTC Address.""" +# -*- coding: utf-8 -*- + +# standard from hashlib import sha256 +import re +# local from .utils import validator -segwit_pattern = re.compile( - r'^(bc|tc)[0-3][02-9ac-hj-np-z]{14,74}$') - - -def validate_segwit_address(addr): - return segwit_pattern.match(addr) +_segwit_pattern = re.compile(r"^(bc|tc)[0-3][02-9ac-hj-np-z]{14,74}$") -def decode_base58(addr): +def _decode_base58(addr: str): + """Decode base58.""" alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - return sum([ - (58 ** e) * alphabet.index(i) - for e, i in enumerate(addr[::-1]) - ]) + return sum((58**enm) * alphabet.index(idx) for enm, idx in enumerate(addr[::-1])) -def validate_old_btc_address(addr): - "Validate P2PKH and P2SH type address" - if not len(addr) in range(25, 35): +def _validate_old_btc_address(addr: str): + """Validate P2PKH and P2SH type address.""" + if len(addr) not in range(25, 35): return False - decoded_bytes = decode_base58(addr).to_bytes(25, "big") - header = decoded_bytes[:-4] - checksum = decoded_bytes[-4:] + decoded_bytes = _decode_base58(addr).to_bytes(25, "big") + header, checksum = decoded_bytes[:-4], decoded_bytes[-4:] return checksum == sha256(sha256(header).digest()).digest()[:4] @validator -def btc_address(value): - """ - Return whether or not given value is a valid bitcoin address. - - If the value is valid bitcoin address this function returns ``True``, - otherwise :class:`~validators.utils.ValidationFailure`. +def btc_address(value: str, /): + """Return whether or not given value is a valid bitcoin address. Full validation is implemented for P2PKH and P2SH addresses. - For segwit addresses a regexp is used to provide a reasonable estimate - on whether the address is valid. + For segwit addresses a regexp is used to provide a reasonable + estimate on whether the address is valid. Examples:: >>> btc_address('3Cwgr2g7vsi1bXDUkpEnVoRLA9w4FZfC69') - True + # Output: True + >>> btc_address('1BvBMsEYstWetqTFn5Au4m4GFg7xJaNVN2') + # Output: ValidationFailure(func=btc_address, args=...) + + Args: + `value`: + [Required] Bitcoin address string to validate. + + Returns: + `True`: + If the value is valid bitcoin address. + `ValidationFailure`: + If the value is an invalid bitcoin address. - :param value: Bitcoin address string to validate """ - if not value or not isinstance(value, str): - return False - if value[:2] in ("bc", "tb"): - return validate_segwit_address(value) - return validate_old_btc_address(value) + if value and type(value) is str: + return ( + _segwit_pattern.match(value) + if value[:2] in ("bc", "tb") + else _validate_old_btc_address(value) + ) + return False