diff --git a/Tribler/Core/Modules/restapi/wallets_endpoint.py b/Tribler/Core/Modules/restapi/wallets_endpoint.py index 4d99096d547..4c3e067a755 100644 --- a/Tribler/Core/Modules/restapi/wallets_endpoint.py +++ b/Tribler/Core/Modules/restapi/wallets_endpoint.py @@ -234,13 +234,13 @@ def render_POST(self, request): """ .. http:get:: /wallets/(string:wallet identifier)/transfer - A GET request to this endpoint will return past transactions of a specific wallet. + A POST request to this endpoint will transfer some units from a wallet to another address. **Example request**: .. sourcecode:: none - curl -X GET http://localhost:8085/wallets/BTC/transfer + curl -X POST http://localhost:8085/wallets/BTC/transfer --data "amount=0.3&destination=mpC1DDgSP4PKc5HxJzQ5w9q6CGLBEQuLsN" **Example response**: diff --git a/Tribler/Test/Community/Market/Wallet/test_btc_wallet.py b/Tribler/Test/Community/Market/Wallet/test_btc_wallet.py index 5e5ae5f5b26..915c226fd59 100644 --- a/Tribler/Test/Community/Market/Wallet/test_btc_wallet.py +++ b/Tribler/Test/Community/Market/Wallet/test_btc_wallet.py @@ -1,3 +1,4 @@ +from jsonrpclib import ProtocolError from twisted.internet.defer import inlineCallbacks, succeed, Deferred from Tribler.Test.Core.base_test import MockObject @@ -143,8 +144,43 @@ def test_get_transactions(self): wallet = BitcoinWallet(self.session_base_dir) mock_daemon = MockObject() mock_server = MockObject() - transactions = [{'value': -1, 'txid': 'a', 'timestamp': 1}, {'value': 1, 'txid': 'a', 'timestamp': 1}] + transactions = [{ + 'value': -1, + 'txid': 'a', + 'timestamp': 1, + 'input_addresses': ['a', 'b'], + 'output_addresses': ['c', 'd'], + 'confirmations': 3 + }, { + 'value': 1, + 'txid': 'b', + 'timestamp': False, # In Electrum, this means that the transaction has not been confirmed yet + 'input_addresses': ['a', 'b'], + 'output_addresses': ['c', 'd'], + 'confirmations': 0 + }] mock_server.run_cmdline = lambda _: transactions mock_daemon.get_server = lambda _: mock_server wallet.get_daemon = lambda: mock_daemon return wallet.get_transactions() + + @deferred(timeout=10) + def test_get_transactions_error(self): + """ + Test whether no transactions are returned when there's a protocol in the JSON RPC protocol + """ + wallet = BitcoinWallet(self.session_base_dir) + mock_daemon = MockObject() + mock_server = MockObject() + + def failing_run_cmdline(*_): + raise ProtocolError() + + mock_server.run_cmdline = failing_run_cmdline + mock_daemon.get_server = lambda _: mock_server + wallet.get_daemon = lambda: mock_daemon + + def verify_transactions(transactions): + self.assertFalse(transactions) + + return wallet.get_transactions().addCallback(verify_transactions) diff --git a/Tribler/Test/Community/Market/test_trade.py b/Tribler/Test/Community/Market/test_trade.py index 9bfcc619887..6c380f8d252 100644 --- a/Tribler/Test/Community/Market/test_trade.py +++ b/Tribler/Test/Community/Market/test_trade.py @@ -22,7 +22,6 @@ def test_to_network(self): # Test for to network self.assertEquals(NotImplemented, self.trade.to_network()) - class ProposedTradeTestSuite(unittest.TestCase): """Proposed trade test cases.""" @@ -61,6 +60,19 @@ def test_from_network(self): self.assertEquals(Quantity(30, 'MC'), data.quantity) self.assertEquals(Timestamp(1462224447.117), data.timestamp) + def test_has_acceptable_price(self): + """ + Test the acceptable price method + """ + self.assertTrue(self.proposed_trade.has_acceptable_price(True, Price(63000, 'BTC'))) + self.assertFalse(self.proposed_trade.has_acceptable_price(False, Price(63000, 'BTC'))) + self.assertTrue(self.proposed_trade.has_acceptable_price(False, Price(64000, 'BTC'))) + self.assertFalse(self.proposed_trade.has_acceptable_price(True, Price(64000, 'BTC'))) + + # Test a price close to the proposed price + self.assertTrue(self.proposed_trade.has_acceptable_price(False, Price(63400.0 - 1e-07, 'BTC'))) + self.assertTrue(self.proposed_trade.has_acceptable_price(True, Price(63400.0 - 1e-07, 'BTC'))) + class DeclinedTradeTestSuite(unittest.TestCase): """Declined trade test cases.""" diff --git a/Tribler/community/market/community.py b/Tribler/community/market/community.py index 80ee65c366a..84de651fd26 100644 --- a/Tribler/community/market/community.py +++ b/Tribler/community/market/community.py @@ -1033,10 +1033,10 @@ def received_proposed_trade(self, _, data): declined_trade = Trade.decline(self.message_repository.next_identity(), Timestamp.now(), proposed_trade, decline_reason) self.logger.debug("Declined trade made with id: %s for proposed trade with id: %s " - "(valid? %s, available quantity of order: %s, reserved: %s, traded: %s)", + "(valid? %s, available quantity of order: %s, reserved: %s, traded: %s), reason: %s", str(declined_trade.message_id), str(proposed_trade.message_id), order.is_valid(), order.available_quantity, order.reserved_quantity, - order.traded_quantity) + order.traded_quantity, decline_reason) self.send_declined_trade(declined_trade) else: self.logger.debug("Proposed trade received with id: %s for order with id: %s", diff --git a/Tribler/community/market/core/trade.py b/Tribler/community/market/core/trade.py index 8bec7f73739..4d9807bac7c 100644 --- a/Tribler/community/market/core/trade.py +++ b/Tribler/community/market/core/trade.py @@ -221,7 +221,13 @@ def has_acceptable_price(self, is_ask, order_price): Return whether this trade proposal has an acceptable price. :rtype: bool """ - return (is_ask and self.price >= order_price) or (not is_ask and self.price <= order_price) + def isclose(price_a, price_b): + price_a = float(price_a) + price_b = float(price_b) + return abs(price_a - price_b) <= 1e-06 + + return (is_ask and (self.price >= order_price or isclose(self.price, order_price))) or \ + (not is_ask and (self.price <= order_price or isclose(self.price, order_price))) def to_network(self): """ diff --git a/Tribler/community/market/wallet/btc_wallet.py b/Tribler/community/market/wallet/btc_wallet.py index 0b945e30fa0..82f4b22906c 100644 --- a/Tribler/community/market/wallet/btc_wallet.py +++ b/Tribler/community/market/wallet/btc_wallet.py @@ -5,6 +5,7 @@ import imp import keyring from Tribler.Core.Utilities.install_dir import get_base_path +from jsonrpclib import ProtocolError from twisted.internet.defer import Deferred, succeed, fail, inlineCallbacks from twisted.internet.task import LoopingCall @@ -150,12 +151,23 @@ def get_balance(self): """ Return the balance of the wallet. """ - divider = 100000000 if self.created: - confirmed, unconfirmed, unmatured = self.wallet.get_balance() + options = {'nolnet': False, 'password': None, 'verbose': False, 'cmd': 'getbalance', + 'wallet_path': self.wallet_file, 'testnet': self.testnet, 'segwit': False, + 'cwd': self.wallet_dir, + 'portable': False} + config = SimpleConfig(options) + + server = self.get_daemon().get_server(config) + result = server.run_cmdline(options) + + confirmed = float(result['confirmed']) + unconfirmed = float(result['unconfirmed']) if 'unconfirmed' in result else 0 + unconfirmed += (float(result['unmatured']) if 'unmatured' in result else 0) + return succeed({ - "available": float(confirmed) / divider, - "pending": float(unconfirmed + unmatured) / divider, + "available": confirmed, + "pending": unconfirmed, "currency": 'BTC' }) else: @@ -226,17 +238,17 @@ def get_transactions(self): config = SimpleConfig(options) server = self.get_daemon().get_server(config) - result = server.run_cmdline(options) + try: + result = server.run_cmdline(options) + except ProtocolError: + self._logger.error("Unable to fetch transactions from BTC wallet!") + return succeed([]) transactions = [] for transaction in result: outgoing = transaction['value'] < 0 - if outgoing: - from_address = self.get_address() - to_address = '' - else: - from_address = '' - to_address = self.get_address() + from_address = ','.join(transaction['input_addresses']) + to_address = ','.join(transaction['output_addresses']) transactions.append({ 'id': transaction['txid'], diff --git a/TriblerGUI/qt_resources/mainwindow.ui b/TriblerGUI/qt_resources/mainwindow.ui index 36a27c05d6d..014b5ee509a 100644 --- a/TriblerGUI/qt_resources/mainwindow.ui +++ b/TriblerGUI/qt_resources/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 875 - 768 + 777 @@ -7982,8 +7982,8 @@ QTabBar::tab:selected { 0 0 - 673 - 341 + 131 + 260 @@ -11196,7 +11196,7 @@ QTabBar::tab:selected { } - 0 + 1 @@ -11273,8 +11273,11 @@ QTabBar::tab:selected { + + QAbstractItemView::NoSelection + - 8 + 0 diff --git a/TriblerGUI/utilities.py b/TriblerGUI/utilities.py index 49a06d1d839..9f3cfa92fc0 100644 --- a/TriblerGUI/utilities.py +++ b/TriblerGUI/utilities.py @@ -48,7 +48,7 @@ def timestamp_to_time(timestamp): diff = today - discovered if diff.days > 0 or today.day != discovered.day: - return discovered.strftime('%d-%m-%Y') + return discovered.strftime('%d-%m-%Y %H:%M') return discovered.strftime('Today %H:%M') diff --git a/TriblerGUI/widgets/marketpage.py b/TriblerGUI/widgets/marketpage.py index 7ba29e577f7..5ff8a82e3f5 100644 --- a/TriblerGUI/widgets/marketpage.py +++ b/TriblerGUI/widgets/marketpage.py @@ -214,6 +214,7 @@ def on_bid(self, bid): def on_transaction_complete(self, transaction): if transaction["mine"]: + transaction = transaction["tx"] main_text = "Transaction with price %f %s and quantity %f %s completed." \ % (transaction["price"], transaction["price_type"], transaction["quantity"], transaction["quantity_type"]) diff --git a/TriblerGUI/widgets/marketwalletspage.py b/TriblerGUI/widgets/marketwalletspage.py index 4c2d254b7aa..c90133edd73 100644 --- a/TriblerGUI/widgets/marketwalletspage.py +++ b/TriblerGUI/widgets/marketwalletspage.py @@ -11,7 +11,7 @@ from TriblerGUI.dialogs.confirmationdialog import ConfirmationDialog from TriblerGUI.tribler_action_menu import TriblerActionMenu from TriblerGUI.tribler_request_manager import TriblerRequestManager -from TriblerGUI.utilities import get_image_path +from TriblerGUI.utilities import get_image_path, timestamp_to_time class MarketWalletsPage(QWidget): @@ -103,6 +103,7 @@ def initialize_wallet_info(self, wallet_id, pressed_button): button.setChecked(False) self.active_wallet = wallet_id + self.window().wallet_info_tabs.setCurrentIndex(0) self.window().wallet_address_label.setText(self.wallets[wallet_id]['address']) # Create a QR code of the wallet address @@ -126,11 +127,11 @@ def initialize_wallet_info(self, wallet_id, pressed_button): self.window().wallet_address_qr_label.setText("QR Code functionality not available!") def load_transactions(self, wallet_id): + self.window().wallet_transactions_list.clear() self.request_mgr = TriblerRequestManager() self.request_mgr.perform_request("wallets/%s/transactions" % wallet_id, self.on_transactions) def on_transactions(self, transactions): - self.window().wallet_transactions_list.clear() for transaction in transactions["transactions"]: item = QTreeWidgetItem(self.window().wallet_transactions_list) item.setText(0, "Sent" if transaction["outgoing"] else "Received") @@ -139,7 +140,8 @@ def on_transactions(self, transactions): item.setText(3, "%g %s" % (transaction["amount"], transaction["currency"])) item.setText(4, "%g %s" % (transaction["fee_amount"], transaction["currency"])) item.setText(5, transaction["id"]) - item.setText(6, transaction["timestamp"]) + timestamp = timestamp_to_time(float(transaction["timestamp"])) if transaction["timestamp"] != "False" else "-" + item.setText(6, timestamp) self.window().wallet_transactions_list.addTopLevelItem(item) def on_add_wallet_clicked(self):