Skip to content

Commit a68ff9a

Browse files
authored
Merge pull request #228 from joe733/workshop
maint: refresh `btc_address` module
2 parents 5dd5ebb + f8fe2d8 commit a68ff9a

File tree

2 files changed

+57
-45
lines changed

2 files changed

+57
-45
lines changed

tests/test_btc_address.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,41 @@
1+
"""Test BTC address."""
12
# -*- coding: utf-8 -*-
3+
4+
# standard
25
import pytest
36

7+
# local
48
from validators import btc_address, ValidationFailure
59

610

711
@pytest.mark.parametrize(
8-
'value',
12+
"value",
913
[
1014
# P2PKH (Pay-to-PubkeyHash) type
11-
'1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
15+
"1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2",
1216
# P2SH (Pay to script hash) type
13-
'3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy',
17+
"3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy",
1418
# Bech32/segwit type
15-
'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq',
16-
'bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9',
19+
"bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq",
20+
"bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9",
1721
],
1822
)
19-
def test_returns_true_on_valid_btc_address(value):
23+
def test_returns_true_on_valid_btc_address(value: str):
24+
"""Test returns true on valid btc address."""
2025
assert btc_address(value)
2126

2227

2328
@pytest.mark.parametrize(
24-
'value',
29+
"value",
2530
[
26-
'ff3Cwgr2g7vsi1bXDUkpEnVoRLA9w4FZfC69',
27-
'b3Cgwgr2g7vsi1bXyjyDUkphEnVoRLA9w4FZfC69',
31+
"ff3Cwgr2g7vsi1bXDUkpEnVoRLA9w4FZfC69",
32+
"b3Cgwgr2g7vsi1bXyjyDUkphEnVoRLA9w4FZfC69",
2833
# incorrect header
29-
'1BvBMsEYstWetqTFn5Au4m4GFg7xJaNVN2',
34+
"1BvBMsEYstWetqTFn5Au4m4GFg7xJaNVN2",
3035
# incorrect checksum
31-
'3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLz',
36+
"3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLz",
3237
],
3338
)
34-
def test_returns_failed_validation_on_invalid_btc_address(value):
39+
def test_returns_failed_validation_on_invalid_btc_address(value: str):
40+
"""Test returns failed validation on invalid btc address."""
3541
assert isinstance(btc_address(value), ValidationFailure)

validators/btc_address.py

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,61 @@
1-
import re
1+
"""BTC Address."""
2+
# -*- coding: utf-8 -*-
3+
4+
# standard
25
from hashlib import sha256
6+
import re
37

8+
# local
49
from .utils import validator
510

6-
segwit_pattern = re.compile(
7-
r'^(bc|tc)[0-3][02-9ac-hj-np-z]{14,74}$')
8-
9-
10-
def validate_segwit_address(addr):
11-
return segwit_pattern.match(addr)
11+
_segwit_pattern = re.compile(r"^(bc|tc)[0-3][02-9ac-hj-np-z]{14,74}$")
1212

1313

14-
def decode_base58(addr):
14+
def _decode_base58(addr: str):
15+
"""Decode base58."""
1516
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
16-
return sum([
17-
(58 ** e) * alphabet.index(i)
18-
for e, i in enumerate(addr[::-1])
19-
])
17+
return sum((58**enm) * alphabet.index(idx) for enm, idx in enumerate(addr[::-1]))
2018

2119

22-
def validate_old_btc_address(addr):
23-
"Validate P2PKH and P2SH type address"
24-
if not len(addr) in range(25, 35):
20+
def _validate_old_btc_address(addr: str):
21+
"""Validate P2PKH and P2SH type address."""
22+
if len(addr) not in range(25, 35):
2523
return False
26-
decoded_bytes = decode_base58(addr).to_bytes(25, "big")
27-
header = decoded_bytes[:-4]
28-
checksum = decoded_bytes[-4:]
24+
decoded_bytes = _decode_base58(addr).to_bytes(25, "big")
25+
header, checksum = decoded_bytes[:-4], decoded_bytes[-4:]
2926
return checksum == sha256(sha256(header).digest()).digest()[:4]
3027

3128

3229
@validator
33-
def btc_address(value):
34-
"""
35-
Return whether or not given value is a valid bitcoin address.
36-
37-
If the value is valid bitcoin address this function returns ``True``,
38-
otherwise :class:`~validators.utils.ValidationFailure`.
30+
def btc_address(value: str, /):
31+
"""Return whether or not given value is a valid bitcoin address.
3932
4033
Full validation is implemented for P2PKH and P2SH addresses.
41-
For segwit addresses a regexp is used to provide a reasonable estimate
42-
on whether the address is valid.
34+
For segwit addresses a regexp is used to provide a reasonable
35+
estimate on whether the address is valid.
4336
4437
Examples::
4538
4639
>>> btc_address('3Cwgr2g7vsi1bXDUkpEnVoRLA9w4FZfC69')
47-
True
40+
# Output: True
41+
>>> btc_address('1BvBMsEYstWetqTFn5Au4m4GFg7xJaNVN2')
42+
# Output: ValidationFailure(func=btc_address, args=...)
43+
44+
Args:
45+
`value`:
46+
[Required] Bitcoin address string to validate.
47+
48+
Returns:
49+
`True`:
50+
If the value is valid bitcoin address.
51+
`ValidationFailure`:
52+
If the value is an invalid bitcoin address.
4853
49-
:param value: Bitcoin address string to validate
5054
"""
51-
if not value or not isinstance(value, str):
52-
return False
53-
if value[:2] in ("bc", "tb"):
54-
return validate_segwit_address(value)
55-
return validate_old_btc_address(value)
55+
if value and type(value) is str:
56+
return (
57+
_segwit_pattern.match(value)
58+
if value[:2] in ("bc", "tb")
59+
else _validate_old_btc_address(value)
60+
)
61+
return False

0 commit comments

Comments
 (0)