forked from LBRYCommunity/lbry-sdk
133 lines
3.4 KiB
Python
133 lines
3.4 KiB
Python
import re
|
|
from typing import TypeVar, Sequence, Optional
|
|
from .constants import COIN
|
|
|
|
|
|
def date_to_julian_day(d):
|
|
return d.toordinal() + 1721424.5
|
|
|
|
|
|
def coins_to_satoshis(coins):
|
|
if not isinstance(coins, str):
|
|
raise ValueError("{coins} must be a string")
|
|
result = re.search(r'^(\d{1,10})\.(\d{1,8})$', coins)
|
|
if result is not None:
|
|
whole, fractional = result.groups()
|
|
return int(whole+fractional.ljust(8, "0"))
|
|
raise ValueError("'{lbc}' is not a valid coin decimal")
|
|
|
|
|
|
def satoshis_to_coins(satoshis):
|
|
coins = '{:.8f}'.format(satoshis / COIN).rstrip('0')
|
|
if coins.endswith('.'):
|
|
return coins+'0'
|
|
else:
|
|
return coins
|
|
|
|
|
|
T = TypeVar('T')
|
|
|
|
|
|
class ReadOnlyList(Sequence[T]):
|
|
|
|
def __init__(self, lst):
|
|
self.lst = lst
|
|
|
|
def __getitem__(self, key):
|
|
return self.lst[key]
|
|
|
|
def __len__(self) -> int:
|
|
return len(self.lst)
|
|
|
|
|
|
def subclass_tuple(name, base):
|
|
return type(name, (base,), {'__slots__': ()})
|
|
|
|
|
|
class cachedproperty:
|
|
|
|
def __init__(self, f):
|
|
self.f = f
|
|
|
|
def __get__(self, obj, objtype):
|
|
obj = obj or objtype
|
|
value = self.f(obj)
|
|
setattr(obj, self.f.__name__, value)
|
|
return value
|
|
|
|
|
|
class ArithUint256:
|
|
# https://github.com/bitcoin/bitcoin/blob/master/src/arith_uint256.cpp
|
|
|
|
__slots__ = '_value', '_compact'
|
|
|
|
def __init__(self, value: int) -> None:
|
|
self._value = value
|
|
self._compact: Optional[int] = None
|
|
|
|
@classmethod
|
|
def from_compact(cls, compact) -> 'ArithUint256':
|
|
size = compact >> 24
|
|
word = compact & 0x007fffff
|
|
if size <= 3:
|
|
return cls(word >> 8 * (3 - size))
|
|
else:
|
|
return cls(word << 8 * (size - 3))
|
|
|
|
@property
|
|
def value(self) -> int:
|
|
return self._value
|
|
|
|
@property
|
|
def compact(self) -> int:
|
|
if self._compact is None:
|
|
self._compact = self._calculate_compact()
|
|
return self._compact
|
|
|
|
@property
|
|
def negative(self) -> int:
|
|
return self._calculate_compact(negative=True)
|
|
|
|
@property
|
|
def bits(self) -> int:
|
|
""" Returns the position of the highest bit set plus one. """
|
|
bits = bin(self._value)[2:]
|
|
for i, d in enumerate(bits):
|
|
if d:
|
|
return (len(bits) - i) + 1
|
|
return 0
|
|
|
|
@property
|
|
def low64(self) -> int:
|
|
return self._value & 0xffffffffffffffff
|
|
|
|
def _calculate_compact(self, negative=False) -> int:
|
|
size = (self.bits + 7) // 8
|
|
if size <= 3:
|
|
compact = self.low64 << 8 * (3 - size)
|
|
else:
|
|
compact = ArithUint256(self._value >> 8 * (size - 3)).low64
|
|
# The 0x00800000 bit denotes the sign.
|
|
# Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
|
|
if compact & 0x00800000:
|
|
compact >>= 8
|
|
size += 1
|
|
assert (compact & ~0x007fffff) == 0
|
|
assert size < 256
|
|
compact |= size << 24
|
|
if negative and compact & 0x007fffff:
|
|
compact |= 0x00800000
|
|
return compact
|
|
|
|
def __mul__(self, x):
|
|
# Take the mod because we are limited to an unsigned 256 bit number
|
|
return ArithUint256((self._value * x) % 2 ** 256)
|
|
|
|
def __truediv__(self, x):
|
|
return ArithUint256(int(self._value / x))
|
|
|
|
def __gt__(self, other):
|
|
return self._value > other
|
|
|
|
def __lt__(self, other):
|
|
return self._value < other
|