From 7a59afbab7e8c63e6336340728955d1a6ccce1da Mon Sep 17 00:00:00 2001 From: vtexier <vit@free.fr> Date: Sun, 16 Feb 2020 18:41:20 +0100 Subject: [PATCH] [enh] update duniterpy calls to version 0.56.0 --- requirements.txt | 2 +- src/sakia/data/connectors/bma.py | 45 ++---- src/sakia/data/connectors/node.py | 153 +++++++++++------- src/sakia/data/entities/connection.py | 8 +- src/sakia/data/entities/node.py | 8 +- src/sakia/data/processors/blockchain.py | 4 +- src/sakia/data/processors/nodes.py | 2 +- src/sakia/gui/dialogs/connection_cfg/view.py | 3 +- src/sakia/gui/dialogs/contact/view.py | 4 +- src/sakia/gui/dialogs/revocation/model.py | 3 +- .../gui/navigation/network/controller.py | 2 +- .../gui/navigation/network/table_model.py | 2 +- src/sakia/gui/sub/transfer/controller.py | 4 +- src/sakia/gui/widgets/context_menu.py | 4 +- src/sakia/services/network.py | 3 +- 15 files changed, 127 insertions(+), 120 deletions(-) diff --git a/requirements.txt b/requirements.txt index a522e088..3af2d740 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ aiohttp==3.6.2 async-timeout==3.0.1 asynctest==0.13.0 attrs==19.3.0 -duniterpy==0.55.1 +duniterpy==0.56.0 jsonschema==3.2.0 networkx==2.4 PyQt5==5.9.2 diff --git a/src/sakia/data/connectors/bma.py b/src/sakia/data/connectors/bma.py index 6cbfc6ec..57ff5b43 100644 --- a/src/sakia/data/connectors/bma.py +++ b/src/sakia/data/connectors/bma.py @@ -1,8 +1,9 @@ import logging import aiohttp from aiohttp import ClientError -from duniterpy.api import bma, errors -from duniterpy.documents import BMAEndpoint, SecuredBMAEndpoint +from duniterpy.api import client, bma, errors +from duniterpy.api.endpoint import BMAEndpoint, SecuredBMAEndpoint +from duniterpy.api.client import parse_error from sakia.errors import NoPeerAvailable from pkg_resources import parse_version from socket import gaierror @@ -25,7 +26,7 @@ async def parse_responses(responses): try: result = ( False, - errors.DuniterError(bma.parse_error(error)).message, + errors.DuniterError(parse_error(error)).message, ) except jsonschema.ValidationError: result = (False, error) @@ -197,7 +198,6 @@ class BmaConnector: nb_verification = min(max(1, 0.66 * len(synced_nodes)), 3) # We try to find agreeing nodes from one 1 to 66% of nodes, max 10 session = aiohttp.ClientSession() - filtered_data = {} try: while ( max([len(nodes) for nodes in answers.values()] + [0]) <= nb_verification @@ -216,31 +216,19 @@ class BmaConnector: str(request.__name__), str(endpoint) ) ) + # create client + _client = client.Client(endpoint, session, proxy=self._user_parameters.proxy()) futures.append( self._verified_request( node, - request( - next( - endpoint.conn_handler( - session, proxy=self._user_parameters.proxy() - ) - ), - **req_args - ), + _client(request, **req_args) ) ) if random_offline_node: futures.append( self._verified_request( random_offline_node[0], - request( - next( - endpoint.conn_handler( - session, proxy=self._user_parameters.proxy() - ) - ), - **req_args - ), + _client(request, **req_args) ) ) except StopIteration: @@ -297,11 +285,8 @@ class BmaConnector: str(request.__name__), str(endpoint) ) ) - async with aiohttp.ClientSession() as session: - json_data = await request( - next(endpoint.conn_handler(session), **req_args) - ) - return json_data + _client = client.Client(endpoint, proxy=self._user_parameters.proxy()) + return await _client(request, **req_args) except errors.DuniterError as e: if e.ucode == errors.HTTP_LIMITATION: self._logger.debug(str(e)) @@ -363,15 +348,9 @@ class BmaConnector: async with aiohttp.ClientSession() as session: for endpoint in endpoints: self._logger.debug("Trying to connect to : " + str(endpoint)) + _client = client.Client(endpoint, proxy=self._user_parameters.proxy()) reply = asyncio.ensure_future( - request( - next( - endpoint.conn_handler( - session, proxy=self._user_parameters.proxy() - ) - ), - **req_args - ) + _client(request, **req_args) ) replies.append(reply) diff --git a/src/sakia/data/connectors/node.py b/src/sakia/data/connectors/node.py index bc33cacc..0b26e82a 100644 --- a/src/sakia/data/connectors/node.py +++ b/src/sakia/data/connectors/node.py @@ -1,8 +1,10 @@ import asyncio import logging import time +import re from asyncio import TimeoutError from socket import gaierror +from typing import Union import aiohttp import jsonschema @@ -10,8 +12,11 @@ from PyQt5.QtCore import QObject, pyqtSignal from aiohttp import ClientError from duniterpy.api import bma, errors -from duniterpy.documents import BlockUID, MalformedDocumentError, BMAEndpoint -from duniterpy.documents.peer import Peer, ConnectionHandler +from duniterpy.api.client import Client +from duniterpy.constants import HOST_REGEX, IPV4_REGEX, IPV6_REGEX +from duniterpy.api.endpoint import BMAEndpoint, SecuredBMAEndpoint +from duniterpy.documents import BlockUID, MalformedDocumentError +from duniterpy.documents.peer import Peer from sakia.decorators import asyncify from sakia.errors import InvalidNodeCurrency from ..entities.node import Node @@ -49,6 +54,8 @@ class NodeConnector(QObject): self._ws_tasks = {"block": None, "peer": None} self._connected = {"block": False, "peer": False} self._user_parameters = user_parameters + if not session: + session = aiohttp.ClientSession() self.session = session self._raw_logger = logging.getLogger("sakia") self._logger = NodeConnectorLoggerAdapter( @@ -72,20 +79,11 @@ class NodeConnector(QObject): :return: A new node :rtype: sakia.core.net.Node """ - http_scheme = "https" if secured else "http" - ws_scheme = "ws" if secured else "wss" - session = aiohttp.ClientSession() - peer_data = await bma.network.peering( - ConnectionHandler( - http_scheme, - ws_scheme, - address, - port, - "", - proxy=user_parameters.proxy(), - session=session, - ) - ) + endpoint = get_bma_endpoint_from_server_address(address, port, secured) + # Create Client from endpoint string in Duniter format + client = Client(endpoint, proxy=user_parameters.proxy()) + + peer_data = client(bma.network.peering) peer = Peer.from_signed_raw( "{0}{1}\n".format(peer_data["raw"], peer_data["signature"]) @@ -103,12 +101,13 @@ class NodeConnector(QObject): ) logging.getLogger("sakia").debug("Node from address : {:}".format(str(node))) - return cls(node, user_parameters, session=session) + return cls(node, user_parameters) @classmethod def from_peer(cls, currency, peer, user_parameters): """ Factory method to get a node from a peer document. + :param str currency: The node currency. None if we don't know\ the currency it should have, for example if its the first one we add :param peer: The peer document @@ -132,8 +131,8 @@ class NodeConnector(QObject): async def safe_request(self, endpoint, request, proxy, req_args={}): try: - conn_handler = next(endpoint.conn_handler(self.session, proxy=proxy)) - data = await request(conn_handler, **req_args) + client = Client(endpoint, self.session, proxy) + data = await client(request, **req_args) return data except errors.DuniterError as e: if e.ucode == 1006: @@ -207,28 +206,30 @@ class NodeConnector(QObject): for endpoint in [e for e in self.node.endpoints if isinstance(e, BMAEndpoint)]: if not self._connected["block"]: try: - conn_handler = next( - endpoint.conn_handler( - self.session, proxy=self._user_parameters.proxy() - ) - ) - ws_connection = bma.ws.block(conn_handler) - async with ws_connection as ws: - self._connected["block"] = True - self._logger.debug("Connected successfully to block ws") - async for msg in ws: - if msg.type == aiohttp.WSMsgType.TEXT: - self._logger.debug("Received a block") - block_data = bma.parse_text( - msg.data, bma.ws.WS_BLOCk_SCHEMA - ) - self.block_found.emit( - BlockUID(block_data["number"], block_data["hash"]) - ) - elif msg.type == aiohttp.WSMsgType.CLOSED: - break - elif msg.type == aiohttp.WSMsgType.ERROR: - break + client = Client(endpoint, self.session, self._user_parameters.proxy()) + + # Create Web Socket connection on block path (async method) + ws = await client(bma.ws.block) # Type: WSConnection + self._connected["block"] = True + self._logger.debug("Connected successfully to block ws") + + loop = True + # Iterate on each message received... + while loop: + # Wait and capture next message + try: + block_data = await ws.receive_json() + jsonschema.validate(block_data, bma.ws.WS_BLOCK_SCHEMA) + self._logger.debug("Received a block") + self.block_found.emit( + BlockUID(block_data["number"], block_data["hash"]) + ) + except TypeError as exception: + self._logger.debug(exception) + + # Close session + await client.close() + except (aiohttp.WSServerHandshakeError, ValueError) as e: self._logger.debug( "Websocket block {0} : {1}".format(type(e).__name__, str(e)) @@ -264,26 +265,27 @@ class NodeConnector(QObject): for endpoint in [e for e in self.node.endpoints if isinstance(e, BMAEndpoint)]: if not self._connected["peer"]: try: - conn_handler = next( - endpoint.conn_handler( - self.session, proxy=self._user_parameters.proxy() - ) - ) - ws_connection = bma.ws.peer(conn_handler) - async with ws_connection as ws: - self._connected["peer"] = True - self._logger.debug("Connected successfully to peer ws") - async for msg in ws: - if msg.type == aiohttp.WSMsgType.TEXT: - self._logger.debug("Received a peer") - peer_data = bma.parse_text( - msg.data, bma.ws.WS_PEER_SCHEMA - ) - self.refresh_peer_data(peer_data) - elif msg.type == aiohttp.WSMsgType.CLOSED: - break - elif msg.type == aiohttp.WSMsgType.ERROR: - break + client = Client(endpoint, self.session, self._user_parameters.proxy()) + + # Create Web Socket connection on peer path (async method) + ws = await client(bma.ws.peer) # Type: WSConnection + self._connected["peer"] = True + self._logger.debug("Connected successfully to peer ws") + + loop = True + # Iterate on each message received... + while loop: + try: + # Wait and capture next message + peer_data = await ws.receive_json() + jsonschema.validate(peer_data, bma.ws.WS_PEER_SCHEMA) + self._logger.debug("Received a peer") + self.refresh_peer_data(peer_data) + except TypeError as exception: + self._logger.debug(exception) + # Close session + await client.close() + except (aiohttp.WSServerHandshakeError, ValueError) as e: self._logger.debug( "Websocket peer {0} : {1}".format(type(e).__name__, str(e)) @@ -400,7 +402,7 @@ class NodeConnector(QObject): for endpoint in [e for e in self.node.endpoints if isinstance(e, BMAEndpoint)]: try: heads_data = await self.safe_request( - endpoint, bma.network.heads, proxy=self._user_parameters.proxy() + endpoint, bma.network.ws2p_heads, proxy=self._user_parameters.proxy() ) if not heads_data: continue @@ -421,3 +423,30 @@ class NodeConnector(QObject): def handle_failure(self, weight=1): self.failure.emit(weight) + + +def get_bma_endpoint_from_server_address(address: str, port: int, secured: bool) -> Union[BMAEndpoint, SecuredBMAEndpoint]: + """ + Return a BMA Endpoint from server address parameters + + :param address: Domain Name or IPV4 ou IPV6 + :param port: Port number + :param secured: True if SSL secured + :return: + """ + server = "" + ipv4 = "" + ipv6 = "" + if re.compile(HOST_REGEX).match(address): + server = address + elif re.compile(IPV4_REGEX).match(address): + ipv4 = address + elif re.compile(IPV6_REGEX).match(address): + ipv6 = address + + if secured: + endpoint = SecuredBMAEndpoint(server, ipv4, ipv6, port, "") + else: + endpoint = BMAEndpoint(server, ipv4, ipv6, port) + + return endpoint diff --git a/src/sakia/data/entities/connection.py b/src/sakia/data/entities/connection.py index 1f87c0ef..13be8ff8 100644 --- a/src/sakia/data/entities/connection.py +++ b/src/sakia/data/entities/connection.py @@ -1,6 +1,6 @@ import attr from duniterpy.documents import block_uid, BlockUID -from duniterpy.key import ScryptParams +from duniterpy.key.scrypt_params import ScryptParams, SCRYPT_PARAMS @attr.s(hash=True) @@ -14,9 +14,9 @@ class Connection: currency = attr.ib(converter=str) pubkey = attr.ib(converter=str) uid = attr.ib(converter=str, default="", cmp=False, hash=False) - scrypt_N = attr.ib(converter=int, default=4096, cmp=False, hash=False) - scrypt_r = attr.ib(converter=int, default=16, cmp=False, hash=False) - scrypt_p = attr.ib(converter=int, default=1, cmp=False, hash=False) + scrypt_N = attr.ib(converter=int, default=SCRYPT_PARAMS['N'], cmp=False, hash=False) + scrypt_r = attr.ib(converter=int, default=SCRYPT_PARAMS['r'], cmp=False, hash=False) + scrypt_p = attr.ib(converter=int, default=SCRYPT_PARAMS['p'], cmp=False, hash=False) blockstamp = attr.ib( converter=block_uid, default=BlockUID.empty(), cmp=False, hash=False ) diff --git a/src/sakia/data/entities/node.py b/src/sakia/data/entities/node.py index aae5c01e..a3bbe474 100644 --- a/src/sakia/data/entities/node.py +++ b/src/sakia/data/entities/node.py @@ -1,14 +1,12 @@ import attr -from duniterpy.documents import block_uid, endpoint +from duniterpy.documents import block_uid +from duniterpy.api.endpoint import endpoint from sakia.helpers import attrs_tuple_of_str def _tuple_of_endpoints(value): - if isinstance(value, tuple): + if isinstance(value, tuple) or isinstance(value, list): return value - elif isinstance(value, list): - l = [endpoint(e) for e in value] - return tuple(l) elif isinstance(value, str): if value: list_of_str = value.split("\n") diff --git a/src/sakia/data/processors/blockchain.py b/src/sakia/data/processors/blockchain.py index 42ec51d5..a1d55408 100644 --- a/src/sakia/data/processors/blockchain.py +++ b/src/sakia/data/processors/blockchain.py @@ -2,11 +2,11 @@ import attr import sqlite3 import logging from sakia.errors import NoPeerAvailable -from ..entities import Blockchain, BlockchainParameters +from ..entities import Blockchain from .nodes import NodesProcessor from ..connectors import BmaConnector from duniterpy.api import bma, errors -from duniterpy.documents import Block, BMAEndpoint +from duniterpy.documents import Block import asyncio diff --git a/src/sakia/data/processors/nodes.py b/src/sakia/data/processors/nodes.py index 50a6dd2f..b9ccb5b9 100644 --- a/src/sakia/data/processors/nodes.py +++ b/src/sakia/data/processors/nodes.py @@ -2,7 +2,7 @@ import attr import sqlite3 from sakia.constants import ROOT_SERVERS from ..entities import Node -from duniterpy.documents import BlockUID, endpoint +from duniterpy.documents import BlockUID import logging import time diff --git a/src/sakia/gui/dialogs/connection_cfg/view.py b/src/sakia/gui/dialogs/connection_cfg/view.py index a84efe17..182786aa 100644 --- a/src/sakia/gui/dialogs/connection_cfg/view.py +++ b/src/sakia/gui/dialogs/connection_cfg/view.py @@ -3,7 +3,8 @@ from PyQt5.QtWidgets import QDialog from PyQt5.QtCore import pyqtSignal, Qt, QElapsedTimer, QDateTime, QCoreApplication from .connection_cfg_uic import Ui_ConnectionConfigurationDialog from .congratulation_uic import Ui_CongratulationPopup -from duniterpy.key import SigningKey, ScryptParams +from duniterpy.key import SigningKey +from duniterpy.key.scrypt_params import ScryptParams from math import ceil, log from sakia.gui.widgets import toast from sakia.decorators import asyncify diff --git a/src/sakia/gui/dialogs/contact/view.py b/src/sakia/gui/dialogs/contact/view.py index 169fb09e..e69f81da 100644 --- a/src/sakia/gui/dialogs/contact/view.py +++ b/src/sakia/gui/dialogs/contact/view.py @@ -7,7 +7,7 @@ from PyQt5.QtWidgets import ( ) from PyQt5.QtCore import QT_TRANSLATE_NOOP, Qt, QModelIndex from .contact_uic import Ui_ContactDialog -from duniterpy.documents.constants import pubkey_regex +from duniterpy.constants import PUBKEY_REGEX import re @@ -54,7 +54,7 @@ class ContactView(QDialog, Ui_ContactDialog): def check_pubkey(self): text = self.edit_pubkey.text() - re_pubkey = re.compile(pubkey_regex) + re_pubkey = re.compile(PUBKEY_REGEX) result = re_pubkey.match(text) if result: self.edit_pubkey.setStyleSheet("") diff --git a/src/sakia/gui/dialogs/revocation/model.py b/src/sakia/gui/dialogs/revocation/model.py index 49c7bfae..52630f88 100644 --- a/src/sakia/gui/dialogs/revocation/model.py +++ b/src/sakia/gui/dialogs/revocation/model.py @@ -1,4 +1,5 @@ -from duniterpy.documents import Revocation, BMAEndpoint, SecuredBMAEndpoint +from duniterpy.documents import Revocation +from duniterpy.api.endpoint import BMAEndpoint, SecuredBMAEndpoint from duniterpy.api import bma, errors from sakia.data.connectors import NodeConnector from asyncio import TimeoutError diff --git a/src/sakia/gui/navigation/network/controller.py b/src/sakia/gui/navigation/network/controller.py index 85568d5d..c0083286 100644 --- a/src/sakia/gui/navigation/network/controller.py +++ b/src/sakia/gui/navigation/network/controller.py @@ -4,7 +4,7 @@ from PyQt5.QtWidgets import QAction, QMenu from PyQt5.QtGui import QCursor, QDesktopServices from PyQt5.QtCore import pyqtSlot, QUrl, QObject from duniterpy.api import bma -from duniterpy.documents import BMAEndpoint +from duniterpy.api.endpoint import BMAEndpoint class NetworkController(QObject): diff --git a/src/sakia/gui/navigation/network/table_model.py b/src/sakia/gui/navigation/network/table_model.py index bd101e30..99447231 100644 --- a/src/sakia/gui/navigation/network/table_model.py +++ b/src/sakia/gui/navigation/network/table_model.py @@ -11,7 +11,7 @@ from PyQt5.QtCore import ( ) from PyQt5.QtGui import QColor, QFont, QIcon from sakia.data.entities import Node -from duniterpy.documents import ( +from duniterpy.api.endpoint import ( BMAEndpoint, SecuredBMAEndpoint, WS2PEndpoint, diff --git a/src/sakia/gui/sub/transfer/controller.py b/src/sakia/gui/sub/transfer/controller.py index 461964ee..1d1a1167 100644 --- a/src/sakia/gui/sub/transfer/controller.py +++ b/src/sakia/gui/sub/transfer/controller.py @@ -4,7 +4,7 @@ import logging from PyQt5.QtCore import Qt, QObject, pyqtSignal from PyQt5.QtWidgets import QApplication, QDialog, QVBoxLayout -from duniterpy.documents.constants import pubkey_regex +from duniterpy.constants import PUBKEY_REGEX from duniterpy.documents import CRCPubkey from sakia.data.processors import ConnectionsProcessor from sakia.decorators import asyncify @@ -199,7 +199,7 @@ class TransferController(QObject): if crc_pubkey.is_valid(): pubkey = crc_pubkey.pubkey except AttributeError: - result = re.compile("^({0})$".format(pubkey_regex)).match( + result = re.compile("^({0})$".format(PUBKEY_REGEX)).match( self.view.pubkey_value() ) if result: diff --git a/src/sakia/gui/widgets/context_menu.py b/src/sakia/gui/widgets/context_menu.py index 5f639945..ae3baed9 100644 --- a/src/sakia/gui/widgets/context_menu.py +++ b/src/sakia/gui/widgets/context_menu.py @@ -3,7 +3,7 @@ import re from PyQt5.QtCore import QObject, pyqtSignal from PyQt5.QtWidgets import QMenu, QAction, QApplication, QMessageBox -from duniterpy.documents.constants import pubkey_regex +from duniterpy.constants import PUBKEY_REGEX from duniterpy.documents.crc_pubkey import CRCPubkey from sakia.data.entities import Identity, Transaction, Dividend from sakia.data.processors import BlockchainProcessor, TransactionsProcessor @@ -130,7 +130,7 @@ class ContextMenu(QObject): @staticmethod def _add_string_actions(menu, str_value): - if re.match(pubkey_regex, str_value): + if re.match(PUBKEY_REGEX, str_value): menu.qmenu.addSeparator().setText(str_value[:7]) copy_pubkey = QAction( menu.qmenu.tr("Copy pubkey to clipboard"), menu.qmenu.parent() diff --git a/src/sakia/services/network.py b/src/sakia/services/network.py index db811789..174754d0 100644 --- a/src/sakia/services/network.py +++ b/src/sakia/services/network.py @@ -5,9 +5,8 @@ import random from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt from duniterpy.api import errors -from duniterpy.documents import MalformedDocumentError from duniterpy.documents.ws2p.heads import * -from duniterpy.documents.peer import BMAEndpoint +from duniterpy.api.endpoint import BMAEndpoint from duniterpy.key import VerifyingKey from sakia.data.connectors import NodeConnector from sakia.data.entities import Node -- GitLab