Astroport.ONE/venv/lib/python3.11/site-packages/duniterpy/helpers/ws2p.py
2024-03-01 16:15:45 +01:00

140 lines
4.6 KiB
Python

# Copyright 2014-2024 Vincent Texier <vit@free.fr>
#
# DuniterPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DuniterPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from typing import Union
import jsonschema
from duniterpy.api import bma, ws2p
from duniterpy.api.client import Client, WSConnection
from duniterpy.api.endpoint import BMAEndpoint, SecuredBMAEndpoint, WS2PEndpoint
from duniterpy.constants import G1_CURRENCY_CODENAME
from duniterpy.documents.ws2p.messages import Ack, Connect, Ok
from duniterpy.key import SigningKey
def handshake(
ws: WSConnection, signing_key: SigningKey, currency: str = G1_CURRENCY_CODENAME
):
"""
Perform ws2p handshake on the web socket connection using the signing_key instance
:param ws: Web socket connection instance
:param signing_key: SigningKey instance
:param currency: Currency codename (default=constants.CURRENCY_CODENAME_G1)
:return:
"""
# START HANDSHAKE #######################################################
logging.debug("\nSTART HANDSHAKE...")
connect_document = Connect(signing_key.pubkey, currency=currency)
connect_message = connect_document.get_signed_json(signing_key)
logging.debug("Send CONNECT message")
ws.send_str(connect_message)
loop = True
remote_connect_document = None
# Iterate on each message received...
while loop:
data = ws.receive_json()
if "auth" in data and data["auth"] == "CONNECT":
jsonschema.validate(data, ws2p.network.WS2P_CONNECT_MESSAGE_SCHEMA)
logging.debug("Received a CONNECT message")
remote_connect_document = Connect(
data["pub"],
challenge=data["challenge"],
signature=data["sig"],
currency=currency,
)
logging.debug("Received CONNECT message signature is valid")
ack_message = Ack(
signing_key.pubkey, remote_connect_document.challenge, currency=currency
).get_signed_json(signing_key)
# Send ACK message
logging.debug("Send ACK message...")
ws.send_str(ack_message)
if "auth" in data and data["auth"] == "ACK":
jsonschema.validate(data, ws2p.network.WS2P_ACK_MESSAGE_SCHEMA)
logging.debug("Received an ACK message")
# Create ACK document from ACK response to verify signature
Ack(
data["pub"],
connect_document.challenge,
signature=data["sig"],
currency=currency,
)
logging.debug("Received ACK message signature is valid")
# If ACK response is ok, create OK message
ok_message = Ok(
signing_key.pubkey, connect_document.challenge, currency=currency
).get_signed_json(signing_key)
# Send OK message
logging.debug("Send OK message...")
ws.send_str(ok_message)
if (
remote_connect_document is not None
and "auth" in data
and data["auth"] == "OK"
):
jsonschema.validate(data, ws2p.network.WS2P_OK_MESSAGE_SCHEMA)
logging.debug("Received an OK message")
Ok(
remote_connect_document.pubkey,
connect_document.challenge,
signature=data["sig"],
currency=currency,
)
logging.debug("Received OK message signature is valid")
# END HANDSHAKE #######################################################
logging.debug("END OF HANDSHAKE\n")
# exit loop
break
def generate_ws2p_endpoint(
bma_endpoint: Union[str, BMAEndpoint, SecuredBMAEndpoint]
) -> WS2PEndpoint:
"""
Retrieve WS2P endpoints from BMA peering
Take the first one found
"""
bma_client = Client(bma_endpoint)
peering = bma_client(bma.network.peering)
for endpoint in peering["endpoints"]:
if endpoint.startswith("WS2P"):
return WS2PEndpoint.from_inline(endpoint)
raise ValueError("No WS2P endpoint found")