diff --git a/lbrynet/daemon/Daemon.py b/lbrynet/daemon/Daemon.py index 436c57fb4..461b390f8 100644 --- a/lbrynet/daemon/Daemon.py +++ b/lbrynet/daemon/Daemon.py @@ -5,12 +5,10 @@ import requests import urllib import json import textwrap -import re from operator import itemgetter from binascii import hexlify, unhexlify from copy import deepcopy -from decimal import Decimal from twisted.internet import defer, reactor from twisted.internet.task import LoopingCall from twisted.python.failure import Failure @@ -48,6 +46,7 @@ from lbrynet.core.SinglePeerDownloader import SinglePeerDownloader from lbrynet.core.client.StandaloneBlobDownloader import StandaloneBlobDownloader from lbrynet.wallet.account import Account as LBCAccount from lbrynet.wallet.manager import LbryWalletManager +from lbrynet.wallet.dewies import dewies_to_lbc, lbc_to_dewies log = logging.getLogger(__name__) requires = AuthJSONRPCServer.requires @@ -1190,7 +1189,7 @@ class Daemon(AuthJSONRPCServer): dewies = yield account.get_balance( 0 if include_unconfirmed else 6 ) - return Decimal(dewies) / COIN + return dewies_to_lbc(dewies) @requires("wallet") @defer.inlineCallbacks @@ -1454,7 +1453,7 @@ class Daemon(AuthJSONRPCServer): return self.get_account_or_error(account_id).get_max_gap() @requires("wallet") - def jsonrpc_account_fund(self, to_account, from_account, amount=0, + def jsonrpc_account_fund(self, to_account, from_account, amount='0.0', everything=False, outputs=1, broadcast=False): """ Transfer some amount (or --everything) to an account from another @@ -1973,7 +1972,6 @@ class Daemon(AuthJSONRPCServer): @requires(STREAM_IDENTIFIER_COMPONENT, WALLET_COMPONENT, EXCHANGE_RATE_MANAGER_COMPONENT, BLOB_COMPONENT, DHT_COMPONENT, RATE_LIMITER_COMPONENT, PAYMENT_RATE_COMPONENT, DATABASE_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) - @defer.inlineCallbacks def jsonrpc_stream_cost_estimate(self, uri, size=None): """ Get estimated cost for a lbry stream @@ -1990,8 +1988,7 @@ class Daemon(AuthJSONRPCServer): (float) Estimated cost in lbry credits, returns None if uri is not resolvable """ - cost = yield self.get_est_cost(uri, size) - defer.returnValue(cost) + return self.get_est_cost(uri, size) @requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) @defer.inlineCallbacks @@ -2045,7 +2042,6 @@ class Daemon(AuthJSONRPCServer): }) @requires(WALLET_COMPONENT) - @defer.inlineCallbacks def jsonrpc_channel_list(self): """ Get certificate claim infos for channels that can be published to @@ -2060,10 +2056,7 @@ class Daemon(AuthJSONRPCServer): (list) ClaimDict, includes 'is_mine' field to indicate if the certificate claim is in the wallet. """ - - result = yield self.wallet_manager.channel_list() - response = yield self._render_response(result) - defer.returnValue(response) + return self.wallet_manager.channel_list() @requires(WALLET_COMPONENT) @defer.inlineCallbacks @@ -3285,13 +3278,11 @@ class Daemon(AuthJSONRPCServer): raise ValueError("Couldn't find account: {}.".format(account_id)) @staticmethod - def get_dewies_or_error(argument: str, amount: str): - if isinstance(amount, str): - result = re.search(r'^(\d{1,10})\.(\d{1,8})$', amount) - if result is not None: - whole, fractional = result.groups() - return int(whole+fractional.ljust(8, "0")) - raise ValueError("Invalid value for '{}' argument: {}".format(argument, amount)) + def get_dewies_or_error(argument: str, lbc: str): + try: + return lbc_to_dewies(lbc) + except ValueError as e: + raise ValueError("Invalid value for '{}': {}".format(argument, e.args[0])) def loggly_time_string(dt): diff --git a/lbrynet/daemon/json_response_encoder.py b/lbrynet/daemon/json_response_encoder.py index dc83e31e1..222c6aa11 100644 --- a/lbrynet/daemon/json_response_encoder.py +++ b/lbrynet/daemon/json_response_encoder.py @@ -3,6 +3,7 @@ from binascii import hexlify from datetime import datetime from json import JSONEncoder from lbrynet.wallet.transaction import Transaction, Output +from lbrynet.wallet.dewies import dewies_to_lbc class JSONResponseEncoder(JSONEncoder): @@ -30,9 +31,9 @@ class JSONResponseEncoder(JSONEncoder): 'height': tx.height, 'inputs': [self.encode_input(txo) for txo in tx.inputs], 'outputs': [self.encode_output(txo) for txo in tx.outputs], - 'total_input': tx.input_sum, - 'total_output': tx.input_sum - tx.fee, - 'total_fee': tx.fee, + 'total_input': dewies_to_lbc(tx.input_sum), + 'total_output': dewies_to_lbc(tx.input_sum - tx.fee), + 'total_fee': dewies_to_lbc(tx.fee), 'hex': hexlify(tx.raw).decode(), } @@ -40,7 +41,7 @@ class JSONResponseEncoder(JSONEncoder): output = { 'txid': txo.tx_ref.id, 'nout': txo.position, - 'amount': txo.amount, + 'amount': dewies_to_lbc(txo.amount), 'address': txo.get_address(self.ledger), 'is_claim': txo.script.is_claim_name, 'is_support': txo.script.is_support_claim, diff --git a/lbrynet/wallet/dewies.py b/lbrynet/wallet/dewies.py new file mode 100644 index 000000000..b705912e1 --- /dev/null +++ b/lbrynet/wallet/dewies.py @@ -0,0 +1,39 @@ +import re +import textwrap +from torba.constants import COIN + + +def lbc_to_dewies(lbc): + if isinstance(lbc, str): + result = re.search(r'^(\d{1,10})\.(\d{1,8})$', lbc) + if result is not None: + whole, fractional = result.groups() + return int(whole+fractional.ljust(8, "0")) + raise ValueError(textwrap.dedent( + r""" + Decimal inputs require a value in the ones place and in the tenths place + separated by a period. The value provided, '{}', is not of the correct + format. + + The following are examples of valid decimal inputs: + + 1.0 + 2.34500 + 4534.4 + 2323434.0000 + + The following are NOT valid: + + 83 + .456 + 123. + """.format(lbc) + )) + + +def dewies_to_lbc(dewies): + lbc = '{:.8f}'.format(dewies / COIN).rstrip('0') + if lbc.endswith('.'): + return lbc+'0' + else: + return lbc diff --git a/lbrynet/wallet/manager.py b/lbrynet/wallet/manager.py index 8576e4960..656c7be7e 100644 --- a/lbrynet/wallet/manager.py +++ b/lbrynet/wallet/manager.py @@ -15,6 +15,7 @@ from .ledger import MainNetLedger from .account import BaseAccount, generate_certificate from .transaction import Transaction from .database import WalletDatabase +from .dewies import dewies_to_lbc log = logging.getLogger(__name__) @@ -259,30 +260,30 @@ class LbryWalletManager(BaseWalletManager): history.append({ 'txid': tx.id, 'timestamp': ts, - 'value': tx.net_account_balance/COIN, - 'fee': tx.fee/COIN, + 'value': dewies_to_lbc(tx.net_account_balance), + 'fee': dewies_to_lbc(tx.fee), 'date': datetime.fromtimestamp(ts).isoformat(' ')[:-3], 'confirmations': headers.height - tx.height, 'claim_info': [{ 'address': txo.get_address(account.ledger), - 'balance_delta': -txo.amount/COIN, - 'amount': txo.amount/COIN, + 'balance_delta': dewies_to_lbc(-txo.amount), + 'amount': dewies_to_lbc(txo.amount), 'claim_id': txo.claim_id, 'claim_name': txo.claim_name, 'nout': txo.position } for txo in tx.my_claim_outputs], 'update_info': [{ 'address': txo.get_address(account.ledger), - 'balance_delta': -txo.amount/COIN, - 'amount': txo.amount/COIN, + 'balance_delta': dewies_to_lbc(-txo.amount), + 'amount': dewies_to_lbc(txo.amount), 'claim_id': txo.claim_id, 'claim_name': txo.claim_name, 'nout': txo.position } for txo in tx.my_update_outputs], 'support_info': [{ 'address': txo.get_address(account.ledger), - 'balance_delta': -txo.amount/COIN, - 'amount': txo.amount/COIN, + 'balance_delta': dewies_to_lbc(-txo.amount), + 'amount': dewies_to_lbc(txo.amount), 'claim_id': txo.claim_id, 'claim_name': txo.claim_name, 'is_tip': False, # TODO: need to add lookup @@ -290,8 +291,8 @@ class LbryWalletManager(BaseWalletManager): } for txo in tx.my_support_outputs], 'abandon_info': [{ 'address': txo.get_address(account.ledger), - 'balance_delta': txo.amount/COIN, - 'amount': txo.amount/COIN, + 'balance_delta': dewies_to_lbc(-txo.amount), + 'amount': dewies_to_lbc(txo.amount), 'claim_id': txo.claim_id, 'claim_name': txo.claim_name, 'nout': txo.position diff --git a/tests/unit/lbrynet_daemon/test_Daemon.py b/tests/unit/lbrynet_daemon/test_Daemon.py index e7ec5a9b5..64e1ce83c 100644 --- a/tests/unit/lbrynet_daemon/test_Daemon.py +++ b/tests/unit/lbrynet_daemon/test_Daemon.py @@ -313,29 +313,3 @@ class TestFileListSorting(unittest.TestCase): setattr(lbry_file, key, faked_attributes[key]) return lbry_file - - -class TestDeweyInputOutput(unittest.TestCase): - - def test_input(self): - self.assertEqual(LBRYDaemon.get_dewies_or_error("", "1.0"), 100000000) - self.assertEqual(LBRYDaemon.get_dewies_or_error("", "2.00000000"), 200000000) - self.assertEqual(LBRYDaemon.get_dewies_or_error("", "2000000000.0"), 200000000000000000) - - def test_invalid_input(self): - with self.assertRaises(ValueError): - LBRYDaemon.get_dewies_or_error("", "1") - with self.assertRaises(ValueError): - LBRYDaemon.get_dewies_or_error("", "-1.0") - with self.assertRaises(ValueError): - LBRYDaemon.get_dewies_or_error("", "10000000000.0") - with self.assertRaises(ValueError): - LBRYDaemon.get_dewies_or_error("", "1.000000000") - with self.assertRaises(ValueError): - LBRYDaemon.get_dewies_or_error("", "-0") - with self.assertRaises(ValueError): - LBRYDaemon.get_dewies_or_error("", "1") - with self.assertRaises(ValueError): - LBRYDaemon.get_dewies_or_error("", ".1") - with self.assertRaises(ValueError): - LBRYDaemon.get_dewies_or_error("", "1e-7") diff --git a/tests/unit/wallet/test_dewies.py b/tests/unit/wallet/test_dewies.py new file mode 100644 index 000000000..ca0177e13 --- /dev/null +++ b/tests/unit/wallet/test_dewies.py @@ -0,0 +1,36 @@ +from twisted.trial import unittest +from lbrynet.wallet.dewies import lbc_to_dewies as l2d, dewies_to_lbc as d2l + + +class TestDeweyConversion(unittest.TestCase): + + def test_good_output(self): + self.assertEqual(d2l(1), "0.00000001") + self.assertEqual(d2l(10**7), "0.1") + self.assertEqual(d2l(2*10**8), "2.0") + self.assertEqual(d2l(2*10**17), "2000000000.0") + + def test_good_input(self): + self.assertEqual(l2d("0.00000001"), 1) + self.assertEqual(l2d("0.1"), 10**7) + self.assertEqual(l2d("1.0"), 10**8) + self.assertEqual(l2d("2.00000000"), 2*10**8) + self.assertEqual(l2d("2000000000.0"), 2*10**17) + + def test_bad_input(self): + with self.assertRaises(ValueError): + l2d("1") + with self.assertRaises(ValueError): + l2d("-1.0") + with self.assertRaises(ValueError): + l2d("10000000000.0") + with self.assertRaises(ValueError): + l2d("1.000000000") + with self.assertRaises(ValueError): + l2d("-0") + with self.assertRaises(ValueError): + l2d("1") + with self.assertRaises(ValueError): + l2d(".1") + with self.assertRaises(ValueError): + l2d("1e-7")