Skip to content

Commit 99f396e

Browse files
ledhed2222mvadari
andauthored
improve currency fix (#269)
* improve currency fix * improve currency fix * improve currency fix * fix tests * fix types * respond to comments * remove extraneous maxDiff in test Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
1 parent 3ba3fc9 commit 99f396e

File tree

4 files changed

+33
-22
lines changed

4 files changed

+33
-22
lines changed

tests/unit/models/currencies/test_issued_currency.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ def test_correct_currency_code_format(self):
1515
self.assertTrue(obj.is_valid())
1616

1717
def test_correct_lower_currency_code_format(self):
18-
# lower case is discouraged but allowed
19-
obj = IssuedCurrency(
20-
currency="usd",
21-
issuer=_ACCOUNT,
22-
)
23-
self.assertTrue(obj.is_valid())
18+
# lower case is not allowed
19+
with self.assertRaises(XRPLModelException):
20+
IssuedCurrency(
21+
currency="usd",
22+
issuer=_ACCOUNT,
23+
)
2424

2525
def test_incorrect_currency_code_format(self):
2626
# the "+" is not allowed in a currency format"

xrpl/constants.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
"""Collection of public constants for XRPL."""
22
from enum import Enum
3+
from re import compile
4+
from typing import Pattern
5+
6+
from typing_extensions import Final
37

48

59
class CryptoAlgorithm(str, Enum):
@@ -13,3 +17,14 @@ class XRPLException(Exception):
1317
"""Base Exception for XRPL library."""
1418

1519
pass
20+
21+
22+
ISO_CURRENCY_REGEX: Final[Pattern[str]] = compile("^[A-Z0-9]{3}$")
23+
"""
24+
:meta private:
25+
"""
26+
27+
HEX_CURRENCY_REGEX: Final[Pattern[str]] = compile("^[A-F0-9]{40}$")
28+
"""
29+
:meta private:
30+
"""

xrpl/core/binarycodec/types/currency.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
"""Codec for currency property inside an XRPL issued currency amount json."""
22
from __future__ import annotations # Requires Python 3.7+
33

4-
import re
54
from typing import Optional, Type
65

76
from typing_extensions import Final
87

8+
from xrpl.constants import HEX_CURRENCY_REGEX, ISO_CURRENCY_REGEX
99
from xrpl.core.binarycodec.exceptions import XRPLBinaryCodecException
1010
from xrpl.core.binarycodec.types.hash160 import Hash160
1111

12-
_ISO_REGEX: Final[re.Pattern[str]] = re.compile("^[A-Z0-9]{3}$")
13-
_HEX_REGEX: Final[re.Pattern[str]] = re.compile("^[A-F0-9]{40}$")
1412
_CURRENCY_CODE_LENGTH: Final[int] = 20 # bytes
1513

1614

1715
def _is_iso_code(value: str) -> bool:
1816
"""Tests if value is a valid 3-char iso code."""
19-
return bool(_ISO_REGEX.fullmatch(value))
17+
return bool(ISO_CURRENCY_REGEX.fullmatch(value))
2018

2119

2220
def _iso_code_from_hex(value: bytes) -> Optional[str]:
@@ -33,7 +31,7 @@ def _iso_code_from_hex(value: bytes) -> Optional[str]:
3331

3432
def _is_hex(value: str) -> bool:
3533
"""Tests if value is a valid 40-char hex string."""
36-
return bool(_HEX_REGEX.fullmatch(value))
34+
return bool(HEX_CURRENCY_REGEX.fullmatch(value))
3735

3836

3937
def _iso_to_bytes(iso: str) -> bytes:
@@ -85,7 +83,7 @@ def __init__(self: Currency, buffer: Optional[bytes] = None) -> None:
8583
code_bytes = self.buffer[12:15]
8684
# Determine whether this currency code is in standard or nonstandard format:
8785
# https://xrpl.org/currency-formats.html#nonstandard-currency-codes
88-
if self.buffer[:2] != bytes(2):
86+
if self.buffer[0] != 0:
8987
# non-standard currency
9088
self._iso = None
9189
elif code_bytes.hex() == "000000":

xrpl/models/currencies/issued_currency.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,20 @@
66
"""
77
from __future__ import annotations
88

9-
import re
109
from dataclasses import dataclass
1110
from typing import Dict
1211

13-
from typing_extensions import Final
14-
12+
from xrpl.constants import HEX_CURRENCY_REGEX, ISO_CURRENCY_REGEX
1513
from xrpl.models.base_model import BaseModel
1614
from xrpl.models.required import REQUIRED
1715
from xrpl.models.utils import require_kwargs_on_init
1816

19-
_CHAR: Final[str] = r"[A-Za-z\d\?!@#\$%\^&\*<>\(\){}\[\]\|]"
20-
_CURRENCY_CODE: Final[str] = f"{_CHAR}{{3}}"
21-
_HEX: Final[str] = f"{_CHAR}{{40}}"
22-
_VALIDATOR: Final[re.Pattern[str]] = re.compile(
23-
f"(?:{_CURRENCY_CODE}|{_HEX})",
24-
)
17+
18+
def _is_valid_currency(candidate: str) -> bool:
19+
return bool(
20+
ISO_CURRENCY_REGEX.fullmatch(candidate)
21+
or HEX_CURRENCY_REGEX.fullmatch(candidate)
22+
)
2523

2624

2725
@require_kwargs_on_init
@@ -52,6 +50,6 @@ def _get_errors(self: IssuedCurrency) -> Dict[str, str]:
5250
errors = super()._get_errors()
5351
if self.currency.upper() == "XRP":
5452
errors["currency"] = "Currency must not be XRP for issued currency"
55-
elif not _VALIDATOR.fullmatch(self.currency):
53+
elif not _is_valid_currency(self.currency):
5654
errors["currency"] = f"Invalid currency {self.currency}"
5755
return errors

0 commit comments

Comments
 (0)