diff --git a/Tribler/Core/APIImplementation/LaunchManyCore.py b/Tribler/Core/APIImplementation/LaunchManyCore.py
index 532d5bf89de..a8aefcda02d 100644
--- a/Tribler/Core/APIImplementation/LaunchManyCore.py
+++ b/Tribler/Core/APIImplementation/LaunchManyCore.py
@@ -878,6 +878,11 @@ def early_shutdown(self):
else:
self._logger.info("lmc: Dispersy failed to shutdown in %.2f seconds", diff)
+ if self.tunnel_community and self.triblerchain_community:
+ # We unload these overlays manually since the triblerchain has to be unloaded after the tunnel overlay.
+ yield self.ipv8.unload_overlay(self.tunnel_community)
+ yield self.ipv8.unload_overlay(self.triblerchain_community)
+
if self.ipv8:
yield self.ipv8.stop(stop_reactor=False)
self.ipv8.endpoint.close()
diff --git a/Tribler/Core/Modules/restapi/market/matchmakers_endpoint.py b/Tribler/Core/Modules/restapi/market/matchmakers_endpoint.py
new file mode 100644
index 00000000000..10560988d4c
--- /dev/null
+++ b/Tribler/Core/Modules/restapi/market/matchmakers_endpoint.py
@@ -0,0 +1,35 @@
+from Tribler.Core.Modules.restapi.market import BaseMarketEndpoint
+from Tribler.Core.Utilities.json_util import dumps
+
+
+class MatchmakersEndpoint(BaseMarketEndpoint):
+ """
+ This class handles requests regarding your known matchmakers in the market community.
+ """
+
+ def render_GET(self, request):
+ """
+ .. http:get:: /market/matchmakers
+
+ A GET request to this endpoint will return all known matchmakers.
+
+ **Example request**:
+
+ .. sourcecode:: none
+
+ curl -X GET http://localhost:8085/market/matchmakers
+
+ **Example response**:
+
+ .. sourcecode:: javascript
+
+ {
+ "matchmakers": [{
+ "ip": "131.249.48.3",
+ "port": 7008
+ }]
+ }
+ """
+ matchmakers = self.session.lm.market_community.matchmakers
+ matchmakers_json = [{"ip": mm.address[0], "port": mm.address[1]} for mm in matchmakers]
+ return dumps({"matchmakers": matchmakers_json})
diff --git a/Tribler/Core/Modules/restapi/market_endpoint.py b/Tribler/Core/Modules/restapi/market_endpoint.py
index c0e7641c35a..fe00e00a635 100644
--- a/Tribler/Core/Modules/restapi/market_endpoint.py
+++ b/Tribler/Core/Modules/restapi/market_endpoint.py
@@ -1,3 +1,4 @@
+from Tribler.Core.Modules.restapi.market.matchmakers_endpoint import MatchmakersEndpoint
from twisted.web import resource
from Tribler.Core.Modules.restapi.market.asks_bids_endpoint import AsksEndpoint, BidsEndpoint
@@ -15,6 +16,6 @@ def __init__(self, session):
self.session = session
child_handler_dict = {"asks": AsksEndpoint, "bids": BidsEndpoint, "transactions": TransactionsEndpoint,
- "orders": OrdersEndpoint}
+ "orders": OrdersEndpoint, "matchmakers": MatchmakersEndpoint}
for path, child_cls in child_handler_dict.iteritems():
self.putChild(path, child_cls(self.session))
diff --git a/Tribler/Core/Modules/restapi/wallets_endpoint.py b/Tribler/Core/Modules/restapi/wallets_endpoint.py
index 4d99096d547..a5ae7b0ccfe 100644
--- a/Tribler/Core/Modules/restapi/wallets_endpoint.py
+++ b/Tribler/Core/Modules/restapi/wallets_endpoint.py
@@ -232,15 +232,15 @@ def __init__(self, session, identifier):
def render_POST(self, request):
"""
- .. http:get:: /wallets/(string:wallet identifier)/transfer
+ .. http:post:: /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/Wallet/test_trustchain_wallet.py b/Tribler/Test/Community/Market/Wallet/test_trustchain_wallet.py
index 2dc393080bc..98e366d943a 100644
--- a/Tribler/Test/Community/Market/Wallet/test_trustchain_wallet.py
+++ b/Tribler/Test/Community/Market/Wallet/test_trustchain_wallet.py
@@ -14,6 +14,7 @@ def setUp(self):
self.initialize(TrustChainCommunity, 2)
self.tc_wallet = TrustchainWallet(self.nodes[0].overlay)
self.tc_wallet.MONITOR_DELAY = 0.01
+ self.tc_wallet.check_negative_balance = True
def create_node(self):
return MockIPv8(u"curve25519", TrustChainCommunity, working_directory=u":memory:")
diff --git a/Tribler/Test/Community/Market/test_community.py b/Tribler/Test/Community/Market/test_community.py
index afab6e57e94..0631cfbba99 100644
--- a/Tribler/Test/Community/Market/test_community.py
+++ b/Tribler/Test/Community/Market/test_community.py
@@ -184,13 +184,38 @@ def test_failing_payment(self):
self.nodes[node_nr].overlay.wallets['DUM2'].transfer = lambda *_: fail(RuntimeError("oops"))
yield self.nodes[0].overlay.create_ask(1, 'DUM1', 1, 'DUM2', 3600)
- yield self.deliver_messages()
yield self.nodes[1].overlay.create_bid(1, 'DUM1', 1, 'DUM2', 3600)
+
yield self.deliver_messages()
self.assertEqual(self.nodes[0].overlay.transaction_manager.find_all()[0].status, "error")
self.assertEqual(self.nodes[1].overlay.transaction_manager.find_all()[0].status, "error")
+ @twisted_wrapper
+ def test_proposed_trade_timeout(self):
+ """
+ Test whether we unreserve the quantity if a proposed trade timeouts
+ """
+ yield self.introduce_nodes()
+
+ self.nodes[0].overlay.decode_map[chr(10)] = lambda *_: None
+
+ ask_order = yield self.nodes[0].overlay.create_ask(1, 'DUM1', 1, 'DUM2', 3600)
+ bid_order = yield self.nodes[1].overlay.create_bid(1, 'DUM1', 1, 'DUM2', 3600)
+
+ yield self.deliver_messages(timeout=.5)
+
+ outstanding = self.nodes[1].overlay.get_outstanding_proposals(bid_order.order_id, ask_order.order_id)
+ self.assertTrue(outstanding)
+ outstanding[0][1].on_timeout()
+
+ yield self.deliver_messages(timeout=.5)
+
+ ask_tick_entry = self.nodes[2].overlay.order_book.get_tick(ask_order.order_id)
+ bid_tick_entry = self.nodes[2].overlay.order_book.get_tick(bid_order.order_id)
+ self.assertEqual(float(bid_tick_entry.reserved_for_matching), 0)
+ self.assertEqual(float(ask_tick_entry.reserved_for_matching), 0)
+
class TestMarketCommunityTwoNodes(TestMarketCommunityBase):
__testing__ = True
@@ -220,3 +245,36 @@ def test_e2e_trade(self):
balance2 = yield self.nodes[1].overlay.wallets['DUM2'].get_balance()
self.assertEqual(balance1['available'], 999)
self.assertEqual(balance2['available'], 1001)
+
+ @twisted_wrapper
+ def test_partial_trade(self):
+ """
+ Test a partial trade
+ """
+ yield self.introduce_nodes()
+
+ yield self.nodes[0].overlay.create_ask(1, 'DUM1', 2, 'DUM2', 3600)
+ bid_order = yield self.nodes[1].overlay.create_bid(1, 'DUM1', 10, 'DUM2', 3600)
+
+ yield self.deliver_messages(timeout=.5)
+
+ # Verify that the trade has been made
+ self.assertEqual(len(self.nodes[0].overlay.transaction_manager.find_all()), 1)
+ self.assertEqual(len(self.nodes[1].overlay.transaction_manager.find_all()), 1)
+
+ # There should be no reserved quantity for the bid tick
+ for node_nr in [0, 1]:
+ bid_tick_entry = self.nodes[node_nr].overlay.order_book.get_tick(bid_order.order_id)
+ self.assertEqual(float(bid_tick_entry.reserved_for_matching), 0)
+
+ yield self.nodes[0].overlay.create_ask(1, 'DUM1', 8, 'DUM2', 3600)
+
+ yield self.deliver_messages(timeout=.5)
+
+ # Verify that the trade has been made
+ self.assertEqual(len(self.nodes[0].overlay.transaction_manager.find_all()), 2)
+ self.assertEqual(len(self.nodes[1].overlay.transaction_manager.find_all()), 2)
+
+ for node_nr in [0, 1]:
+ self.assertEqual(len(self.nodes[node_nr].overlay.order_book.asks), 0)
+ self.assertEqual(len(self.nodes[node_nr].overlay.order_book.bids), 0)
diff --git a/Tribler/Test/Community/Market/test_orderbook.py b/Tribler/Test/Community/Market/test_orderbook.py
index 85400ba375c..1859d52a0a1 100644
--- a/Tribler/Test/Community/Market/test_orderbook.py
+++ b/Tribler/Test/Community/Market/test_orderbook.py
@@ -187,13 +187,15 @@ def test_update_ticks(self):
"trader_id": str(self.ask.order_id.trader_id),
"order_number": int(self.ask.order_id.order_number),
"quantity": 3,
- "quantity_type": self.ask.quantity.wallet_id
+ "quantity_type": self.ask.quantity.wallet_id,
+ "traded_quantity": 3
}
bid_dict = {
"trader_id": str(self.bid.order_id.trader_id),
"order_number": int(self.bid.order_id.order_number),
"quantity": 3,
- "quantity_type": self.bid.quantity.wallet_id
+ "quantity_type": self.bid.quantity.wallet_id,
+ "traded_quantity": 3
}
self.order_book.get_tick(self.ask.order_id).reserve_for_matching(Quantity(3, self.ask.quantity.wallet_id))
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/Test/Core/Modules/RestApi/test_market_endpoint.py b/Tribler/Test/Core/Modules/RestApi/test_market_endpoint.py
index 38b0e64d0a8..ee37740f380 100644
--- a/Tribler/Test/Core/Modules/RestApi/test_market_endpoint.py
+++ b/Tribler/Test/Core/Modules/RestApi/test_market_endpoint.py
@@ -261,3 +261,16 @@ def on_response(response):
self.should_check_equality = False
return self.do_request('market/orders/1/cancel', request_type='POST', expected_code=200)\
.addCallback(on_response)
+
+ @deferred(timeout=10)
+ def test_get_matchmakers(self):
+ """
+ Test the request to fetch known matchmakers
+ """
+ def on_response(response):
+ json_response = json.loads(response)
+ self.assertGreaterEqual(len(json_response['matchmakers']), 1)
+
+ self.session.lm.market_community.matchmakers.add(self.session.lm.market_community.my_peer)
+ self.should_check_equality = False
+ return self.do_request('market/matchmakers', expected_code=200).addCallback(on_response)
diff --git a/Tribler/community/market/community.py b/Tribler/community/market/community.py
index 23857c5b2d3..8458e3a0ec0 100644
--- a/Tribler/community/market/community.py
+++ b/Tribler/community/market/community.py
@@ -67,7 +67,7 @@ def on_timeout(self):
if self.match_id:
# Inform the matchmaker about the failed trade
match_message = self.community.incoming_match_messages[self.match_id]
- self.community.send_decline_match_message(self.match_id, match_message.payload.matchmaker_trader_id,
+ self.community.send_decline_match_message(self.match_id, match_message.matchmaker_trader_id,
DeclineMatchReason.OTHER)
@@ -114,7 +114,7 @@ def __init__(self, *args, **kwargs):
self.use_local_address = False
self.matching_enabled = True
self.message_repository = MemoryMessageRepository(self.mid)
- self.use_incremental_payments = True
+ self.use_incremental_payments = False
self.matchmakers = set()
self.pending_matchmaker_deferreds = []
self.request_cache = RequestCache()
@@ -529,6 +529,7 @@ def received_half_block(self, source_address, data):
TransactionNumber(block.transaction["tx"]["transaction_number"]))
transaction = self.transaction_manager.find_by_id(transaction_id)
if transaction and self.market_database.get_linked(block):
+ self.notify_transaction_complete(transaction.to_dictionary(), mine=True)
self.send_transaction_completed(transaction, block)
self.process_market_block(block)
@@ -798,8 +799,8 @@ def received_match(self, source_address, data):
auth, _, payload = self._ez_unpack_auth(MatchPayload, data)
peer = Peer(auth.public_key_bin, source_address)
- self.logger.debug("We received a match message for order %s.%s",
- TraderId(self.mid), payload.recipient_order_number)
+ self.logger.debug("We received a match message for order %s.%s (matched quantity: %s)",
+ TraderId(self.mid), payload.recipient_order_number, payload.match_quantity)
# We got a match, check whether we can respond to this match
self.update_ip(payload.matchmaker_trader_id, source_address)
@@ -1032,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",
@@ -1356,7 +1357,7 @@ def on_payment_error(failure):
When a payment fails, log the error and still send a payment message to inform the other party that the
payment has failed.
"""
- self.logger.error("Payment of %f to %s failed: %s", transfer_amount,
+ self.logger.error("Payment of %s to %s failed: %s", transfer_amount,
str(transaction.partner_incoming_address), failure.value)
self.send_payment_message(PaymentId(''), transaction, payment_tup, False)
@@ -1449,7 +1450,7 @@ def on_tx_done_signed(block):
"""
We received the signed block from the counterparty, wrap everything up
"""
- self.notify_transaction_complete(transaction)
+ self.notify_transaction_complete(transaction.to_dictionary(), mine=True)
self.send_transaction_completed(transaction, block)
def build_tx_done_block(other_order_dict):
@@ -1481,14 +1482,16 @@ def abort_transaction(self, transaction):
"""
self.logger.error("Aborting transaction %s", transaction.transaction_id)
order = self.order_manager.order_repository.find_by_id(transaction.order_id)
- order.release_quantity_for_tick(transaction.partner_order_id,
- transaction.total_quantity - transaction.transferred_quantity)
- self.order_manager.order_repository.update(order)
+ if (transaction.total_quantity - transaction.transferred_quantity) > \
+ Quantity(0, transaction.total_quantity.wallet_id):
+ order.release_quantity_for_tick(transaction.partner_order_id,
+ transaction.total_quantity - transaction.transferred_quantity)
+ self.order_manager.order_repository.update(order)
- def notify_transaction_complete(self, transaction):
+ def notify_transaction_complete(self, tx_dict, mine=False):
if self.tribler_session:
self.tribler_session.notifier.notify(NTFY_MARKET_ON_TRANSACTION_COMPLETE, NTFY_UPDATE, None,
- transaction.to_dictionary())
+ {"tx": tx_dict, "mine": mine})
def send_transaction_completed(self, transaction, block):
"""
@@ -1535,6 +1538,8 @@ def on_transaction_completed_bc_message(self, block1, _):
if not self.is_matchmaker:
return
+ self.notify_transaction_complete(tx_dict["tx"])
+
# Update ticks in order book, release the reserved quantity
quantity = Quantity(tx_dict["tx"]["quantity"], tx_dict["tx"]["quantity_type"])
self.order_book.update_ticks(tx_dict["ask"], tx_dict["bid"], quantity, unreserve=False)
diff --git a/Tribler/community/market/core/matching_engine.py b/Tribler/community/market/core/matching_engine.py
index 146dffb2a01..8c17bf4793f 100644
--- a/Tribler/community/market/core/matching_engine.py
+++ b/Tribler/community/market/core/matching_engine.py
@@ -237,8 +237,8 @@ def _search_for_quantity_in_price_level(self, order_id, tick_entry, quantity_to_
def _search_for_quantity_in_price_level_total(self, tick_entry, quantity_to_trade):
trading_quantity = quantity_to_trade
- self._logger.debug("Match with the id (%s) was found: price %i, quantity %i",
- str(tick_entry.order_id), float(tick_entry.price), int(trading_quantity))
+ self._logger.debug("Match with the id (%s) was found: price %f, quantity %f",
+ str(tick_entry.order_id), float(tick_entry.price), float(trading_quantity))
return [(self.get_unique_match_id(), tick_entry, trading_quantity)]
@@ -250,8 +250,8 @@ def _search_for_quantity_in_price_level_partial(self, order_id, tick_entry, quan
tick_entry.is_blocked_for_matching(order_id):
quantity_to_trade -= matched_quantity
- self._logger.debug("Match with the id (%s) was found: price %i, quantity %i",
- str(tick_entry.order_id), float(tick_entry.price), int(matched_quantity))
+ self._logger.debug("Match with the id (%s) was found: price %f, quantity %f",
+ str(tick_entry.order_id), float(tick_entry.price), float(matched_quantity))
matching_ticks = [(self.get_unique_match_id(), tick_entry, matched_quantity)]
diff --git a/Tribler/community/market/core/orderbook.py b/Tribler/community/market/core/orderbook.py
index 5c41e62c2e4..cc5085979f2 100644
--- a/Tribler/community/market/core/orderbook.py
+++ b/Tribler/community/market/core/orderbook.py
@@ -117,9 +117,9 @@ def update_ticks(self, ask_order_dict, bid_order_dict, traded_quantity, unreserv
str(ask_order_id), str(bid_order_id), str(traded_quantity))
# Update ask tick
- new_ask_quantity = Quantity(ask_order_dict["quantity"] - float(traded_quantity),
+ new_ask_quantity = Quantity(ask_order_dict["quantity"] - ask_order_dict["traded_quantity"],
ask_order_dict["quantity_type"])
- if self.tick_exists(ask_order_id) and new_ask_quantity < self.get_tick(ask_order_id).quantity:
+ if self.tick_exists(ask_order_id) and new_ask_quantity <= self.get_tick(ask_order_id).quantity:
tick = self.get_tick(ask_order_id)
tick.quantity = new_ask_quantity
if unreserve:
@@ -133,9 +133,9 @@ def update_ticks(self, ask_order_dict, bid_order_dict, traded_quantity, unreserv
self.insert_ask(ask)
# Update bid tick
- new_bid_quantity = Quantity(bid_order_dict["quantity"] - float(traded_quantity),
+ new_bid_quantity = Quantity(bid_order_dict["quantity"] - bid_order_dict["traded_quantity"],
bid_order_dict["quantity_type"])
- if self.tick_exists(bid_order_id) and new_bid_quantity < self.get_tick(bid_order_id).quantity:
+ if self.tick_exists(bid_order_id) and new_bid_quantity <= self.get_tick(bid_order_id).quantity:
tick = self.get_tick(bid_order_id)
tick.quantity = new_bid_quantity
if unreserve:
diff --git a/Tribler/community/market/core/side.py b/Tribler/community/market/core/side.py
index d1d208429b0..19f5e3db3b6 100644
--- a/Tribler/community/market/core/side.py
+++ b/Tribler/community/market/core/side.py
@@ -117,11 +117,12 @@ def remove_tick(self, order_id):
assert isinstance(order_id, OrderId), type(order_id)
tick = self.get_tick(order_id)
- tick.cancel_all_pending_tasks()
- tick.price_level().remove_tick(tick)
- if len(tick.price_level()) == 0: # Last tick for that price
- self._remove_price_level(tick.price, tick.quantity.wallet_id)
- del self._tick_map[order_id]
+ if tick:
+ tick.cancel_all_pending_tasks()
+ tick.price_level().remove_tick(tick)
+ if len(tick.price_level()) == 0: # Last tick for that price
+ self._remove_price_level(tick.price, tick.quantity.wallet_id)
+ del self._tick_map[order_id]
def get_price_level_list(self, price_wallet_id, quantity_wallet_id):
"""
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 de46d8c46fc..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
@@ -88,7 +89,7 @@ def start_daemon(self):
if not fd:
return
- self.daemon = self.get_daemon().Daemon(config, fd)
+ self.daemon = self.get_daemon().Daemon(config, fd, is_gui=False)
self.daemon.start()
def open_wallet(self):
@@ -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'],
@@ -247,7 +259,7 @@ def get_transactions(self):
'fee_amount': 0.0,
'currency': 'BTC',
'timestamp': str(transaction['timestamp']),
- 'description': ''
+ 'description': 'Confirmations: %d' % transaction['confirmations']
})
return succeed(transactions)
diff --git a/Tribler/community/market/wallet/tc_wallet.py b/Tribler/community/market/wallet/tc_wallet.py
index 6c044ed5c4b..c80e13080fe 100644
--- a/Tribler/community/market/wallet/tc_wallet.py
+++ b/Tribler/community/market/wallet/tc_wallet.py
@@ -20,7 +20,7 @@ def __init__(self, tc_community):
self.tc_community = tc_community
self.created = True
- self.check_negative_balance = True
+ self.check_negative_balance = False
self.transaction_history = []
def get_name(self):
diff --git a/Tribler/community/triblertunnel/dispatcher.py b/Tribler/community/triblertunnel/dispatcher.py
index c6dc12d1a3c..633b165f252 100644
--- a/Tribler/community/triblertunnel/dispatcher.py
+++ b/Tribler/community/triblertunnel/dispatcher.py
@@ -46,8 +46,6 @@ def on_incoming_from_tunnel(self, community, circuit, origin, data, force=False)
socks5_data = conversion.encode_udp_packet(
0, 0, conversion.ADDRESS_TYPE_IPV4, origin[0], origin[1], data)
return session._udp_socket.sendDatagram(socks5_data)
- else:
- self._logger.error("UDP socket for socks server not available!")
return False
diff --git a/Tribler/pyipv8 b/Tribler/pyipv8
index b336cc086cc..bcb9cb61ef3 160000
--- a/Tribler/pyipv8
+++ b/Tribler/pyipv8
@@ -1 +1 @@
-Subproject commit b336cc086cceff20e9596ae4c8012f55027d42af
+Subproject commit bcb9cb61ef38e36c48ced676b90daeba6c3ac4d9
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/tribler_window.py b/TriblerGUI/tribler_window.py
index e27d96ac4b2..703ec5e3f8a 100644
--- a/TriblerGUI/tribler_window.py
+++ b/TriblerGUI/tribler_window.py
@@ -467,8 +467,8 @@ def load_token_balance(self):
def received_token_balance(self, statistics):
statistics = statistics["statistics"]
if 'latest_block' in statistics:
- balance = statistics["latest_block"]["transaction"]["total_up"] - \
- statistics["latest_block"]["transaction"]["total_down"]
+ balance = (statistics["latest_block"]["transaction"]["total_up"] - \
+ statistics["latest_block"]["transaction"]["total_down"]) / 1024 / 1024
self.token_balance_label.setText("%d" % balance)
else:
self.token_balance_label.setText("0")
@@ -736,6 +736,7 @@ def show_force_shutdown():
if self.tray_icon:
self.tray_icon.deleteLater()
self.show_loading_screen()
+ self.hide_status_bar()
self.loading_text_label.setText("Shutting down...")
self.shutdown_timer = QTimer()
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 af4035b2486..5ff8a82e3f5 100644
--- a/TriblerGUI/widgets/marketpage.py
+++ b/TriblerGUI/widgets/marketpage.py
@@ -213,17 +213,22 @@ def on_bid(self, bid):
self.update_filter_bids_list()
def on_transaction_complete(self, transaction):
- main_text = "Transaction with price %f %s and quantity %f %s completed." \
- % (transaction["price"], transaction["price_type"],
- transaction["quantity"], transaction["quantity_type"])
- self.window().tray_icon.showMessage("Transaction completed", main_text)
- self.window().hide_status_bar()
+ 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"])
+ self.window().tray_icon.showMessage("Transaction completed", main_text)
+ self.window().hide_status_bar()
- # Reload wallets
- self.load_wallets()
+ # Reload wallets
+ self.load_wallets()
- # Reload transactions
- self.window().market_transactions_page.load_transactions()
+ # Reload transactions
+ self.window().market_transactions_page.load_transactions()
+ else:
+ self.load_asks()
+ self.load_bids()
def on_iom_input_required(self, event_dict):
self.dialog = IomInputDialog(self.window().stackedWidget, event_dict['bank_name'], event_dict['input'])
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):
diff --git a/electrum b/electrum
index d062792493d..c26cc844f5d 160000
--- a/electrum
+++ b/electrum
@@ -1 +1 @@
-Subproject commit d062792493dad38c92b0ef3326ec2176517a39ad
+Subproject commit c26cc844f5dd3a91452dd84568dcacfec23679e2
diff --git a/systemd/tribler.service b/systemd/tribler.service
new file mode 100644
index 00000000000..08f422e2fe7
--- /dev/null
+++ b/systemd/tribler.service
@@ -0,0 +1,21 @@
+[Unit]
+Description="Tribler runner"
+After=network.target
+
+[Service]
+ProtectSystem=yes
+PrivateTmp=true
+Type=simple
+User=tribler
+Group=tribler
+Restart=always
+Environment=HOME=/var/lib/tribler/%I
+Environment=EXTRA_TRIBLER_ARGS=--
+Environment=PYTHONPATH=/opt/tribler
+
+WorkingDirectory=/opt/tribler
+ExecStartPre=/bin/mkdir -p ${HOME}
+ExecStart=/usr/bin/twistd --nodaemon --pidfile= tribler ${EXTRA_TRIBLER_ARGS}
+
+[Install]
+WantedBy=multi-user.target