Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add validator for trx addresses #384

Merged
merged 3 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ Note to self: Breaking changes must increment either

-->

## 0.30.0 (2024-07-04)

_**Breaking**_

> No breaking changes were introduced in this version.

_**Features**_

- feat: add validator for trx addresses by @msamsami in [#384](https://github.com/python-validators/validators/pull/384)

_**Maintenance**_

- maint: bump version by @msamsami in [#384](https://github.com/python-validators/validators/pull/384)

**Full Changelog**: [`0.29.0...0.30.0`](https://github.com/python-validators/validators/compare/0.29.0...0.30.0)

msamsami marked this conversation as resolved.
Show resolved Hide resolved
---

## 0.29.0 (2024-07-01)

_**Breaking**_ ⚠️
Expand All @@ -25,6 +43,8 @@ _**Maintenance**_

**Full Changelog**: [`0.28.3...0.29.0`](https://github.com/python-validators/validators/compare/0.28.3...0.29.0)

---

## 0.28.3 (2024-05-25)

_**Breaking**_
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

| Version | Supported |
| ---------- | ------------------ |
| `>=0.29.0` | :white_check_mark: |
| `>=0.30.0` | :white_check_mark: |

## Reporting a Vulnerability

Expand Down
1 change: 1 addition & 0 deletions docs/api/crypto_addresses.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

::: validators.crypto_addresses.btc_address
::: validators.crypto_addresses.eth_address
::: validators.crypto_addresses.trx_address
1 change: 1 addition & 0 deletions docs/api/crypto_addresses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ crypto_addresses
.. module:: validators.crypto_addresses
.. autofunction:: btc_address
.. autofunction:: eth_address
.. autofunction:: trx_address
5 changes: 3 additions & 2 deletions src/validators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .card import amex, card_number, diners, discover, jcb, mastercard, unionpay, visa
from .country import calling_code, country_code, currency
from .cron import cron
from .crypto_addresses import btc_address, eth_address
from .crypto_addresses import btc_address, eth_address, trx_address
from .domain import domain
from .email import email
from .encoding import base58, base64
Expand Down Expand Up @@ -39,6 +39,7 @@
# crypto_addresses
"btc_address",
"eth_address",
"trx_address",
# cards
"amex",
"card_number",
Expand Down Expand Up @@ -104,4 +105,4 @@
"validator",
)

__version__ = "0.29.0"
__version__ = "0.30.0"
3 changes: 2 additions & 1 deletion src/validators/crypto_addresses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
# local
from .btc_address import btc_address
from .eth_address import eth_address
from .trx_address import trx_address

__all__ = ("btc_address", "eth_address")
__all__ = ("btc_address", "eth_address", "trx_address")
62 changes: 62 additions & 0 deletions src/validators/crypto_addresses/trx_address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""TRX Address."""

# standard
import hashlib
import re

# local
from validators.utils import validator


def _base58_decode(addr: str) -> bytes:
"""Decode a base58 encoded address."""
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
num = 0
for char in addr:
num = num * 58 + alphabet.index(char)
return num.to_bytes(25, byteorder="big")


def _validate_trx_checksum_address(addr: str) -> bool:
"""Validate TRX type checksum address."""
if len(addr) != 34:
return False

try:
address = _base58_decode(addr)
except ValueError:
return False

if len(address) != 25 or address[0] != 0x41:
return False

check_sum = hashlib.sha256(hashlib.sha256(address[:-4]).digest()).digest()[:4]
return address[-4:] == check_sum


@validator
def trx_address(value: str, /):
"""Return whether or not given value is a valid tron address.

Full validation is implemented for TRC20 tron addresses.

Examples:
>>> trx_address('TLjfbTbpZYDQ4EoA4N5CLNgGjfbF8ZWz38')
# Output: True
>>> trx_address('TR2G7Rm4vFqF8EpY4U5xdLdQ7XgJ2U8Vd')
# Output: ValidationError(func=trx_address, args=...)

Args:
value:
Tron address string to validate.

Returns:
(Literal[True]): If `value` is a valid tron address.
(ValidationError): If `value` is an invalid tron address.
"""
if not value:
return False

return re.compile(r"^[T][a-km-zA-HJ-NP-Z1-9]{33}$").match(
value
) and _validate_trx_checksum_address(value)
54 changes: 54 additions & 0 deletions tests/crypto_addresses/test_trx_address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Test TRX address."""

# external
import pytest

# local
from validators import ValidationError, trx_address


@pytest.mark.parametrize(
"value",
[
"TLjfbTbpZYDQ4EoA4N5CLNgGjfbF8ZWz38",
"TDQ6C92wuNqvMWE967sMptCFaXq77uj1PF",
"TFuGbxCQGSL4oLnJzVsen844LDwFbrUY4e",
"TFAPKADDRhkSe3v27CsR8TZSjN8eJ8ycDK",
"TSJHywLNva2MNjCD5iYfn5QAKD9Rk5Ncit",
"TEi1qhi5LuTicg1u9oAstyXCSf5uibSyqo",
"TAGvx5An6VBeHTu91cQwdABNcAYMRPcP4n",
"TXbE5tXTejqT3Q47sYKCDb9NJDm3xrFpab",
"TMTxQWNuWHXvHcYXc5D1wQhFmZFJijAxcG",
"TPHgw9E8QYM3esNWih5KVnUVpUHwLTPfpA",
"TFFLtBTi9jdaGwV3hznjCmPYaJme5AeqwU",
"TC74QG8tbtixG5Raa4fEifywgjrFs45fNz",
],
)
def test_returns_true_on_valid_trx_address(value: str):
"""Test returns true on valid trx address."""
assert trx_address(value)


@pytest.mark.parametrize(
"value",
[
"T12345678901234567890123456789012345",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678",
"TR2G7Rm4vFqF8EpY4U5xdLdQ7XgJ2U8Vd",
"TP6ah2v5mdsj8Z3hGz1yDMvDq7BzEbK8o",
"TQmmhp6uz2Xre8yL3FsPYZyo4mhtw4vg4XX",
"TQNy2C6VHJPk4P32bsEX3QSGx2Qqm4J2k9",
"TP6ah2v5mdsj8Z3hGz1yDMvDq7BzEbK8oN",
"TSTVdfU1x4L7K3Bc3v5C28Gp2J1rPyeL3f",
"THPByuCzvU5QER9j2NC2mUQ2JPyRCam4e7",
"TW5eZqUZgdW4rxFKAKsc2ryJbfFA94WXvD",
"TR2G7Rm4vFqF8EpY4U5xdLdQ7XgJ2U8Vdd",
"tQmmhp6uz2Xre8yL3FsPYZyo4mhtw4vg4X",
"TR2G7Rm4vFqF8EpY4U5xdLdQ7Xg",
"TQmmhp6uz2Xre8yL3FsPYZyo4mhtw4vg4x",
"my-trox-address.trx",
],
)
def test_returns_failed_validation_on_invalid_trx_address(value: str):
"""Test returns failed validation on invalid trx address."""
assert isinstance(trx_address(value), ValidationError)
Loading