diff --git a/lib/ucoinpy/__init__.py b/lib/ucoinpy/__init__.py index 354362cd909050638e202e6df9b96384bf8fc7c0..3b1345bc8e6afcb68399f864af138a40ab4cbe0f 100644 --- a/lib/ucoinpy/__init__.py +++ b/lib/ucoinpy/__init__.py @@ -19,3 +19,5 @@ PROTOCOL_VERSION="1" MANAGED_API=["BASIC_MERKLED_API"] + +from . import api, documents, key \ No newline at end of file diff --git a/src/cutecoin/core/net/api/bma/access.py b/src/cutecoin/core/net/api/bma/access.py index ad6437f4d980f506b5e2065f847361a779a1c434..6975c69efa05b90236f9b1ec78bd682bd1c8c365 100644 --- a/src/cutecoin/core/net/api/bma/access.py +++ b/src/cutecoin/core/net/api/bma/access.py @@ -1,6 +1,6 @@ from PyQt5.QtCore import QObject, pyqtSlot from PyQt5.QtNetwork import QNetworkReply -from . import blockchain, network, node, tx, wot, ConnectionHandler +from . import blockchain, ConnectionHandler from .....tools.exceptions import NoPeerAvailable import logging import json @@ -276,10 +276,7 @@ class BmaAccess(QObject): nodes = self._network.synced_nodes if len(nodes) > 0: node = random.choice(nodes) - server = node.endpoint.conn_handler().server - port = node.endpoint.conn_handler().port - conn_handler = ConnectionHandler(self._network.network_manager, server, port) - req = request(conn_handler, **req_args) + req = request(node.endpoint.conn_handler(self._network.network_manager), **req_args) reply = req.get(**get_args) return reply else: diff --git a/src/cutecoin/core/net/endpoint.py b/src/cutecoin/core/net/endpoint.py new file mode 100644 index 0000000000000000000000000000000000000000..7ddbf792520195a42a29522a05cfa5ece496a1f9 --- /dev/null +++ b/src/cutecoin/core/net/endpoint.py @@ -0,0 +1,76 @@ +import re +import ucoinpy +from .api.bma import ConnectionHandler + +HANDLED_API=["BASIC_MERKLED_API"] + +class Endpoint(): + """ + Describing endpoints + """ + def __init__(self, pyendpoint): + self.pyendpoint = pyendpoint + + @staticmethod + def from_inline(inline): + for api in HANDLED_API: + if (inline.startswith(api)): + if (api == "BASIC_MERKLED_API"): + return BMAEndpoint.from_inline(inline) + return UnknownEndpoint.from_inline(inline) + + @property + def server(self): + return self.pyendpoint.server + + @property + def ipv4(self): + return self.pyendpoint.ipv4 + + @property + def ipv6(self): + return self.pyendpoint.ipv6 + + @property + def port(self): + return self.pyendpoint.port + + +class UnknownEndpoint(Endpoint): + + def __init__(self, pyendpoint): + super().__init__(pyendpoint) + + @classmethod + def from_inline(cls, inline): + endpoint = ucoinpy.documents.peer.UnknownEndpoint.from_inline(inline) + return cls(endpoint) + + def inline(self): + self.pyendpoint.inline() + + +class BMAEndpoint(Endpoint): + re_inline = re.compile('^BASIC_MERKLED_API(?: ([a-z0-9-_.]*(?:.[a-zA-Z])))?(?: ((?:[0-9.]{1,4}){4}))?(?: ((?:[0-9a-f:]{4,5}){4,8}))?(?: ([0-9]+))$') + + def __init__(self, pyendpoint): + super().__init__(pyendpoint) + + @classmethod + def from_inline(cls, inline): + endpoint = ucoinpy.documents.peer.BMAEndpoint.from_inline(inline) + return cls(endpoint) + + def inline(self): + return self.pyendpoint.inline() + + def conn_handler(self, network_manager): + if self.server: + return ConnectionHandler(network_manager, + self.pyendpoint.server, self.pyendpoint.port) + elif self.ipv4: + return ConnectionHandler(network_manager, self.pyendpoint.ipv4, + self.pyendpoint.port) + else: + return ConnectionHandler(network_manager, + self.pyendpoint.ipv6, self.pyendpoint.port) diff --git a/src/cutecoin/core/net/network.py b/src/cutecoin/core/net/network.py index d23edee9eb686a3a022a4e19b9817ca72f727acb..f9c3890b078816f1a3b470a5e0ae4ff7ddefc02f 100644 --- a/src/cutecoin/core/net/network.py +++ b/src/cutecoin/core/net/network.py @@ -10,7 +10,7 @@ import time import asyncio from ucoinpy.documents.peer import Peer -from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QCoreApplication, QTimer +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QTimer class Network(QObject): diff --git a/src/cutecoin/core/net/node.py b/src/cutecoin/core/net/node.py index 3497eb2cca5e0904afc86496cb6b1d247c5db1de..4cfec79bd0099d39467ce313ce12f53252869150 100644 --- a/src/cutecoin/core/net/node.py +++ b/src/cutecoin/core/net/node.py @@ -4,9 +4,10 @@ Created on 21 févr. 2015 @author: inso """ -from ucoinpy.documents.peer import Peer, BMAEndpoint, Endpoint +from ucoinpy.documents.peer import Peer from ...tools.exceptions import InvalidNodeCurrency from ..net.api import bma as qtbma +from ..net.endpoint import Endpoint, BMAEndpoint from ..net.api.bma import ConnectionHandler import asyncio @@ -289,9 +290,7 @@ class Node(QObject): self.refresh_summary() def refresh_block(self): - conn_handler = ConnectionHandler(self.network_manager, - self.endpoint.conn_handler().server, - self.endpoint.conn_handler().port) + conn_handler = self.endpoint.conn_handler(self.network_manager) logging.debug("Requesting {0}".format(conn_handler)) reply = qtbma.blockchain.Current(conn_handler).get() @@ -321,9 +320,7 @@ class Node(QObject): logging.debug("Error in block reply") def refresh_informations(self): - conn_handler = ConnectionHandler(self.network_manager, - self.endpoint.conn_handler().server, - self.endpoint.conn_handler().port) + conn_handler = self.endpoint.conn_handler(self.network_manager) peering_reply = qtbma.network.Peering(conn_handler).get() peering_reply.finished.connect(self.handle_peering_reply) @@ -356,9 +353,7 @@ class Node(QObject): logging.debug("Error in peering reply") def refresh_summary(self): - conn_handler = ConnectionHandler(self.network_manager, - self.endpoint.conn_handler().server, - self.endpoint.conn_handler().port) + conn_handler = self.endpoint.conn_handler(self.network_manager) summary_reply = qtbma.node.Summary(conn_handler).get() summary_reply.finished.connect(self.handle_summary_reply) @@ -375,9 +370,7 @@ class Node(QObject): self.version = summary_data["ucoin"]["version"] def refresh_uid(self): - conn_handler = ConnectionHandler(self.network_manager, - self.endpoint.conn_handler().server, - self.endpoint.conn_handler().port) + conn_handler = self.endpoint.conn_handler(self.network_manager) uid_reply = qtbma.wot.Lookup(conn_handler, self.pubkey).get() uid_reply.finished.connect(self.handle_uid_reply) uid_reply.error.connect(lambda code: logging.debug("Error : {0}".format(code))) @@ -410,9 +403,7 @@ class Node(QObject): logging.debug("error in uid reply") def refresh_peers(self): - conn_handler = ConnectionHandler(self.network_manager, - self.endpoint.conn_handler().server, - self.endpoint.conn_handler().port) + conn_handler = self.endpoint.conn_handler(self.network_manager) reply = qtbma.network.peering.Peers(conn_handler).get(leaves='true') reply.finished.connect(self.handle_peers_reply) @@ -430,9 +421,7 @@ class Node(QObject): leaves = [leaf for leaf in peers_data['leaves'] if leaf not in self._last_merkle['leaves']] for leaf_hash in leaves: - conn_handler = ConnectionHandler(self.network_manager, - self.endpoint.conn_handler().server, - self.endpoint.conn_handler().port) + conn_handler = ConnectionHandler(self.network_manager) leaf_reply = qtbma.network.peering.Peers(conn_handler).get(leaf=leaf_hash) leaf_reply.finished.connect(self.handle_leaf_reply) self._last_merkle = {'root' : peers_data['root'], diff --git a/src/cutecoin/gui/currency_tab.py b/src/cutecoin/gui/currency_tab.py index 71af87f5e99980a475b73d8f99e019ede9bb9f43..258f37b59c3cb6bc01a509ddbbb1ea0320920d07 100644 --- a/src/cutecoin/gui/currency_tab.py +++ b/src/cutecoin/gui/currency_tab.py @@ -66,7 +66,8 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): self.tab_informations = InformationsTabWidget(self.app.current_account, self.community) - self.tab_network = NetworkTabWidget(self.community) + self.tab_network = NetworkTabWidget(self.app, + self.community) self.tabs_account.addTab(self.tab_wallets, QIcon(':/icons/wallet_icon'), diff --git a/src/cutecoin/gui/network_tab.py b/src/cutecoin/gui/network_tab.py index 1816942cc45a17b3c4b64616cdb860c3f1dd25bd..6f30a4cfe036697ec70e3ffafb47c3b7037a4f4c 100644 --- a/src/cutecoin/gui/network_tab.py +++ b/src/cutecoin/gui/network_tab.py @@ -5,10 +5,11 @@ Created on 20 févr. 2015 """ import logging -from PyQt5.QtGui import QCursor +from PyQt5.QtGui import QCursor, QDesktopServices from PyQt5.QtWidgets import QWidget, QMenu, QAction -from PyQt5.QtCore import Qt, QModelIndex, pyqtSlot +from PyQt5.QtCore import Qt, QModelIndex, pyqtSlot, QUrl from ..models.network import NetworkTableModel, NetworkFilterProxyModel +from ..core.net.api import bma as qtbma from ..gen_resources.network_tab_uic import Ui_NetworkTabWidget @@ -17,11 +18,19 @@ class NetworkTabWidget(QWidget, Ui_NetworkTabWidget): classdocs """ - def __init__(self, community): + def __init__(self, app, community): """ - Constructor + Constructore of a network tab. + + :param cutecoin.core.Application app: The application + :param cutecoin.core.Community community: The community + :return: A new network tab. + :rtype: NetworkTabWidget """ super().__init__() + self.app = app + self.community = community + self.setupUi(self) model = NetworkTableModel(community) proxy = NetworkFilterProxyModel(self) @@ -30,7 +39,6 @@ class NetworkTabWidget(QWidget, Ui_NetworkTabWidget): self.table_network.sortByColumn(0, Qt.DescendingOrder) self.table_network.resizeColumnsToContents() community.network.nodes_changed.connect(self.refresh_nodes) - self.community = community @pyqtSlot() def refresh_nodes(self): @@ -38,28 +46,35 @@ class NetworkTabWidget(QWidget, Ui_NetworkTabWidget): self.table_network.model().sourceModel().modelReset.emit() def node_context_menu(self, point): - index = self.table_network.indexAt(point) - model = self.table_network.model() - if index.row() < model.rowCount(QModelIndex()): - source_index = model.mapToSource(index) - is_root_col = model.sourceModel().columns_types.index('is_root') - is_root_index = model.sourceModel().index(source_index.row(), is_root_col) - is_root = model.sourceModel().data(is_root_index, Qt.DisplayRole) - - menu = QMenu() - if is_root: - unset_root = QAction(self.tr("Unset root node"), self) - unset_root.triggered.connect(self.unset_root_node) - unset_root.setData(self.community.network.root_node_index(source_index.row())) - if len(self.community.network.root_nodes) > 1: - menu.addAction(unset_root) - else: - set_root = QAction(self.tr("Set as root node"), self) - set_root.triggered.connect(self.set_root_node) - set_root.setData(self.community.network.nodes[source_index.row()]) - menu.addAction(set_root) - # Show the context menu. - menu.exec_(QCursor.pos()) + index = self.table_network.indexAt(point) + model = self.table_network.model() + if index.row() < model.rowCount(QModelIndex()): + source_index = model.mapToSource(index) + is_root_col = model.sourceModel().columns_types.index('is_root') + is_root_index = model.sourceModel().index(source_index.row(), is_root_col) + is_root = model.sourceModel().data(is_root_index, Qt.DisplayRole) + + menu = QMenu() + if is_root: + unset_root = QAction(self.tr("Unset root node"), self) + unset_root.triggered.connect(self.unset_root_node) + unset_root.setData(self.community.network.root_node_index(source_index.row())) + if len(self.community.network.root_nodes) > 1: + menu.addAction(unset_root) + else: + set_root = QAction(self.tr("Set as root node"), self) + set_root.triggered.connect(self.set_root_node) + set_root.setData(self.community.network.nodes[source_index.row()]) + menu.addAction(set_root) + + if self.app.preferences['expert_mode']: + open_in_browser = QAction(self.tr("Open in browser"), self) + open_in_browser.triggered.connect(self.open_node_in_browser) + open_in_browser.setData(self.community.network.nodes[source_index.row()]) + menu.addAction(open_in_browser) + + # Show the context menu. + menu.exec_(QCursor.pos()) @pyqtSlot() def set_root_node(self): @@ -71,5 +86,12 @@ class NetworkTabWidget(QWidget, Ui_NetworkTabWidget): index = self.sender().data() self.community.network.remove_root_node(index) + @pyqtSlot() + def open_node_in_browser(self): + node = self.sender().data() + peering = qtbma.network.Peering(node.endpoint) + url = QUrl(peering.reverse_url("/peering")) + QDesktopServices.openUrl(url) +