util.format_satoshis: floating-point paranoia
This commit is contained in:
@@ -101,7 +101,7 @@ def satoshis(amount):
|
||||
return int(COIN*to_decimal(amount)) if amount is not None else None
|
||||
|
||||
|
||||
def format_satoshis(x: Union[str, float, int, Decimal, None]) -> Optional[str]:
|
||||
def format_satoshis(x: Union[float, int, Decimal, None]) -> Optional[str]:
|
||||
"""
|
||||
input: satoshis as a Number
|
||||
output: str formatted as bitcoin amount
|
||||
|
||||
@@ -234,6 +234,8 @@ def to_decimal(x: Union[str, float, int, Decimal]) -> Decimal:
|
||||
# Decimal('41754.681')
|
||||
if isinstance(x, Decimal):
|
||||
return x
|
||||
if isinstance(x, int):
|
||||
return Decimal(x)
|
||||
return Decimal(str(x))
|
||||
|
||||
|
||||
@@ -800,8 +802,10 @@ def format_satoshis_plain(
|
||||
if is_max_allowed and parse_max_spend(x):
|
||||
return f'max({x})'
|
||||
assert isinstance(x, (int, float, Decimal)), f"{x!r} should be a number"
|
||||
# TODO(ghost43) just hard-fail if x is a float. do we even use floats for money anywhere?
|
||||
x = to_decimal(x)
|
||||
scale_factor = pow(10, decimal_point)
|
||||
return "{:.8f}".format(Decimal(x) / scale_factor).rstrip('0').rstrip('.')
|
||||
return "{:.8f}".format(x / scale_factor).rstrip('0').rstrip('.')
|
||||
|
||||
|
||||
# Check that Decimal precision is sufficient.
|
||||
@@ -833,8 +837,10 @@ def format_satoshis(
|
||||
if parse_max_spend(x):
|
||||
return f'max({x})'
|
||||
assert isinstance(x, (int, float, Decimal)), f"{x!r} should be a number"
|
||||
# TODO(ghost43) just hard-fail if x is a float. do we even use floats for money anywhere?
|
||||
x = to_decimal(x)
|
||||
# lose redundant precision
|
||||
x = Decimal(x).quantize(Decimal(10) ** (-precision))
|
||||
x = x.quantize(Decimal(10) ** (-precision))
|
||||
# format string
|
||||
overall_precision = decimal_point + precision # max digits after final decimal point
|
||||
decimal_format = "." + str(overall_precision) if overall_precision > 0 else ""
|
||||
|
||||
@@ -1759,7 +1759,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
||||
fee = self.adb.get_tx_fee(tx_hash)
|
||||
if fee is not None:
|
||||
size = tx.estimated_size()
|
||||
fee_per_byte = fee / size
|
||||
fee_per_byte = Decimal(fee) / size
|
||||
extra.append(format_fee_satoshis(fee_per_byte) + f" {util.UI_UNIT_NAME_FEERATE_SAT_PER_VB}")
|
||||
if fee is not None and height in (TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED) \
|
||||
and self.network and self.network.has_fee_mempool():
|
||||
|
||||
@@ -185,6 +185,7 @@ class TestCommands(ElectrumTestCase):
|
||||
self.assertEqual(format_satoshis(Decimal(123.456)), "0.00000123")
|
||||
self.assertEqual(format_satoshis(Decimal(123.5)), "0.00000124")
|
||||
self.assertEqual(format_satoshis(Decimal(123.789)), "0.00000124")
|
||||
self.assertEqual(format_satoshis(41754.681), "0.00041755")
|
||||
|
||||
|
||||
class TestCommandsTestnet(ElectrumTestCase):
|
||||
|
||||
Reference in New Issue
Block a user