2017-11-25 22:59:32 +00:00
|
|
|
from decimal import Decimal
|
2017-12-27 00:49:59 +00:00
|
|
|
import sys
|
2017-11-25 22:59:32 +00:00
|
|
|
|
|
|
|
PICONERO = Decimal('0.000000000001')
|
|
|
|
|
2017-12-27 00:49:59 +00:00
|
|
|
if sys.version_info < (3,):
|
|
|
|
_integer_types = (int, long,)
|
2018-01-06 22:12:42 +00:00
|
|
|
_str_types = (str, bytes, unicode)
|
2017-12-27 00:49:59 +00:00
|
|
|
else:
|
|
|
|
_integer_types = (int,)
|
2018-01-06 22:12:42 +00:00
|
|
|
_str_types = (str, bytes)
|
2017-12-27 00:49:59 +00:00
|
|
|
|
|
|
|
|
2017-11-25 22:59:32 +00:00
|
|
|
def to_atomic(amount):
|
2017-11-29 03:38:29 +00:00
|
|
|
"""Convert Monero decimal to atomic integer of piconero."""
|
2017-11-25 22:59:32 +00:00
|
|
|
return int(amount * 10**12)
|
|
|
|
|
|
|
|
def from_atomic(amount):
|
2017-11-29 03:38:29 +00:00
|
|
|
"""Convert atomic integer of piconero to Monero decimal."""
|
2017-11-25 22:59:32 +00:00
|
|
|
return (Decimal(amount) * PICONERO).quantize(PICONERO)
|
2017-11-29 03:38:29 +00:00
|
|
|
|
|
|
|
def as_monero(amount):
|
|
|
|
"""Return the amount rounded to maximal Monero precision."""
|
|
|
|
return Decimal(amount).quantize(PICONERO)
|
2017-12-27 00:49:59 +00:00
|
|
|
|
2018-01-06 22:12:42 +00:00
|
|
|
|
|
|
|
class PaymentID(object):
|
2018-02-15 20:32:26 +00:00
|
|
|
"""
|
|
|
|
A class that validates Monero payment ID.
|
|
|
|
|
|
|
|
Payment IDs can be used as str or int across the module, however this class
|
|
|
|
offers validation as well as simple conversion and comparison to those two
|
|
|
|
primitive types.
|
|
|
|
|
|
|
|
:param payment_id: the payment ID as integer or hexadecimal string
|
|
|
|
"""
|
2018-01-06 22:12:42 +00:00
|
|
|
_payment_id = None
|
|
|
|
|
|
|
|
def __init__(self, payment_id):
|
|
|
|
if isinstance(payment_id, PaymentID):
|
|
|
|
payment_id = int(payment_id)
|
|
|
|
if isinstance(payment_id, _str_types):
|
|
|
|
payment_id = int(payment_id, 16)
|
|
|
|
elif not isinstance(payment_id, _integer_types):
|
|
|
|
raise TypeError("payment_id must be either int or hexadecimal str or bytes, "
|
2018-01-07 00:26:30 +00:00
|
|
|
"is {0}".format(type(payment_id)))
|
|
|
|
if payment_id.bit_length() > 256:
|
|
|
|
raise ValueError("payment_id {0} is more than 256 bits long".format(payment_id))
|
2018-01-06 22:12:42 +00:00
|
|
|
self._payment_id = payment_id
|
|
|
|
|
|
|
|
def is_short(self):
|
|
|
|
"""Returns True if payment ID is short enough to be included
|
2018-02-15 20:32:26 +00:00
|
|
|
in :class:`IntegratedAddress <monero.address.IntegratedAddress>`."""
|
2018-01-06 22:12:42 +00:00
|
|
|
return self._payment_id.bit_length() <= 64
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
if self.is_short():
|
|
|
|
return "{:016x}".format(self._payment_id)
|
|
|
|
return "{:064x}".format(self._payment_id)
|
|
|
|
|
|
|
|
def __int__(self):
|
|
|
|
return self._payment_id
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
if isinstance(other, PaymentID):
|
|
|
|
return int(self) == int(other)
|
|
|
|
elif isinstance(other, _integer_types):
|
|
|
|
return int(self) == other
|
|
|
|
elif isinstance(other, _str_types):
|
|
|
|
return str(self) == other
|
2018-01-30 08:43:08 +00:00
|
|
|
return super(PaymentID, self).__eq__(other)
|