mirror of https://github.com/nucypher/nucypher.git
commit
4131c8ca0c
|
@ -6,6 +6,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import shutil
|
import shutil
|
||||||
|
import ssl
|
||||||
import sys
|
import sys
|
||||||
from typing import Tuple, ClassVar
|
from typing import Tuple, ClassVar
|
||||||
|
|
||||||
|
@ -1168,10 +1169,8 @@ def ursula(config,
|
||||||
config.operating_mode = "federated" if ursula_config.federated_only else "decentralized"
|
config.operating_mode = "federated" if ursula_config.federated_only else "decentralized"
|
||||||
click.secho("Running in {} mode".format(config.operating_mode), fg='blue')
|
click.secho("Running in {} mode".format(config.operating_mode), fg='blue')
|
||||||
|
|
||||||
# ursula_config.read_known_nodes(known_metadata_dir=additional_nodes)
|
# Bootnodes, Seeds, Known Nodes
|
||||||
# if additional_nodes: # Secondary override
|
ursula_config.get_bootnodes()
|
||||||
# click.secho("Loaded additional known nodes", color='blue')
|
|
||||||
|
|
||||||
quantity_known_nodes = len(ursula_config.known_nodes)
|
quantity_known_nodes = len(ursula_config.known_nodes)
|
||||||
if quantity_known_nodes > 0:
|
if quantity_known_nodes > 0:
|
||||||
click.secho("Loaded {} known nodes from storages".format(quantity_known_nodes, fg='blue'))
|
click.secho("Loaded {} known nodes from storages".format(quantity_known_nodes, fg='blue'))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
from collections import namedtuple
|
||||||
from os.path import abspath, dirname
|
from os.path import abspath, dirname
|
||||||
|
|
||||||
from appdirs import AppDirs
|
from appdirs import AppDirs
|
||||||
|
@ -6,12 +7,17 @@ from appdirs import AppDirs
|
||||||
import nucypher
|
import nucypher
|
||||||
|
|
||||||
# Base Filepaths
|
# Base Filepaths
|
||||||
|
|
||||||
BASE_DIR = abspath(dirname(dirname(nucypher.__file__)))
|
BASE_DIR = abspath(dirname(dirname(nucypher.__file__)))
|
||||||
PROJECT_ROOT = abspath(dirname(nucypher.__file__))
|
PROJECT_ROOT = abspath(dirname(nucypher.__file__))
|
||||||
APP_DIR = AppDirs("nucypher", "NuCypher")
|
APP_DIR = AppDirs("nucypher", "NuCypher")
|
||||||
DEFAULT_CONFIG_ROOT = APP_DIR.user_data_dir
|
DEFAULT_CONFIG_ROOT = APP_DIR.user_data_dir
|
||||||
|
|
||||||
|
# Bootnodes
|
||||||
|
Bootnode = namedtuple('Bootnode', ['checksum_address', 'rest_url'])
|
||||||
|
BOOTNODES = (
|
||||||
|
Bootnode('0xDbf2Bc4b81eB46CdDfa52348Ecf3c142841267E0', 'https://18.223.117.103:9151'),
|
||||||
|
)
|
||||||
|
|
||||||
# Test Constants # TODO: Tidy up filepath here
|
# Test Constants # TODO: Tidy up filepath here
|
||||||
TEST_CONTRACTS_DIR = os.path.join(BASE_DIR, 'tests', 'blockchain', 'eth', 'contracts', 'contracts')
|
TEST_CONTRACTS_DIR = os.path.join(BASE_DIR, 'tests', 'blockchain', 'eth', 'contracts', 'contracts')
|
||||||
NUMBER_OF_URSULAS_IN_MOCK_NETWORK = 10
|
NUMBER_OF_URSULAS_IN_MOCK_NETWORK = 10
|
||||||
|
|
|
@ -1,18 +1,29 @@
|
||||||
import binascii
|
import binascii
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
import time
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import requests
|
||||||
from constant_sorrow import constants
|
from constant_sorrow import constants
|
||||||
|
from cryptography import x509
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from cryptography.hazmat.primitives.serialization import Encoding
|
||||||
|
|
||||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, BASE_DIR
|
from nucypher.characters.lawful import Ursula
|
||||||
from nucypher.config.keyring import NucypherKeyring
|
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, BASE_DIR, BOOTNODES
|
||||||
|
from nucypher.config.keyring import NucypherKeyring, _write_tls_certificate, _read_tls_public_certificate
|
||||||
from nucypher.config.storages import NodeStorage, InMemoryNodeStorage
|
from nucypher.config.storages import NodeStorage, InMemoryNodeStorage
|
||||||
from nucypher.crypto.powers import CryptoPowerUp
|
from nucypher.crypto.powers import CryptoPowerUp
|
||||||
|
from nucypher.crypto.signing import signature_splitter
|
||||||
from nucypher.network.middleware import RestMiddleware
|
from nucypher.network.middleware import RestMiddleware
|
||||||
|
from nucypher.network.nodes import VerifiableNode
|
||||||
|
|
||||||
|
|
||||||
class NodeConfiguration:
|
class NodeConfiguration:
|
||||||
|
@ -185,8 +196,11 @@ class NodeConfiguration:
|
||||||
storage_type = storage_payload[NodeStorage._TYPE_LABEL]
|
storage_type = storage_payload[NodeStorage._TYPE_LABEL]
|
||||||
storage_class = NODE_STORAGES[storage_type]
|
storage_class = NODE_STORAGES[storage_type]
|
||||||
node_storage = storage_class.from_payload(payload=storage_payload,
|
node_storage = storage_class.from_payload(payload=storage_payload,
|
||||||
|
character_class=cls._Character,
|
||||||
|
federated_only=payload['federated_only'],
|
||||||
serializer=cls.NODE_SERIALIZER,
|
serializer=cls.NODE_SERIALIZER,
|
||||||
deserializer=cls.NODE_DESERIALIZER)
|
deserializer=cls.NODE_DESERIALIZER)
|
||||||
|
|
||||||
payload.update(dict(node_storage=node_storage))
|
payload.update(dict(node_storage=node_storage))
|
||||||
return cls(**{**payload, **overrides})
|
return cls(**{**payload, **overrides})
|
||||||
|
|
||||||
|
@ -354,7 +368,7 @@ class NodeConfiguration:
|
||||||
|
|
||||||
def read_known_nodes(self) -> set:
|
def read_known_nodes(self) -> set:
|
||||||
"""Read known nodes from metadata, and use them when producing a character"""
|
"""Read known nodes from metadata, and use them when producing a character"""
|
||||||
known_nodes = self.node_storage.all()
|
known_nodes = self.node_storage.all(federated_only=self.federated_only)
|
||||||
return known_nodes
|
return known_nodes
|
||||||
|
|
||||||
def read_keyring(self, *args, **kwargs):
|
def read_keyring(self, *args, **kwargs):
|
||||||
|
@ -421,3 +435,76 @@ class NodeConfiguration:
|
||||||
|
|
||||||
self.log.info("Successfully wrote registry to {}".format(output_filepath))
|
self.log.info("Successfully wrote registry to {}".format(output_filepath))
|
||||||
return output_filepath
|
return output_filepath
|
||||||
|
|
||||||
|
def __learn_from_bootnode(self, bootnode):
|
||||||
|
parsed_url = urlparse(bootnode.rest_url)
|
||||||
|
|
||||||
|
# Pre-fetch certificate
|
||||||
|
self.log.info("Fetching bootnode {} TLS certificate".format(bootnode.checksum_address))
|
||||||
|
bootnode_certificate = ssl.get_server_certificate((parsed_url.hostname, parsed_url.port))
|
||||||
|
certificate = x509.load_pem_x509_certificate(bootnode_certificate.encode(),
|
||||||
|
backend=default_backend())
|
||||||
|
|
||||||
|
# Write certificate
|
||||||
|
filename = '{}.{}'.format(bootnode.checksum_address, Encoding.PEM.name.lower())
|
||||||
|
certificate_filepath = os.path.join(self.known_certificates_dir, filename)
|
||||||
|
_write_tls_certificate(certificate=certificate, full_filepath=certificate_filepath, force=True)
|
||||||
|
self.log.info("Saved bootnode {} TLS certificate".format(bootnode.checksum_address))
|
||||||
|
|
||||||
|
# Learn from Bootnode
|
||||||
|
response = self.network_middleware.get_nodes_via_rest(url=parsed_url.netloc,
|
||||||
|
certificate_filepath=certificate_filepath)
|
||||||
|
self.log.info("Retrieved bootnode data from {}".format(bootnode.checksum_address))
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise RuntimeError("Bad response from bootnode {}".format(bootnode.rest_url))
|
||||||
|
|
||||||
|
signature, nodes = signature_splitter(response.content, return_remainder=True)
|
||||||
|
node_list = Ursula.batch_from_bytes(nodes, federated_only=self.federated_only) # TODO: 466
|
||||||
|
self.log.debug("Learned from Bootnode {}|{}".format(bootnode.checksum_address, parsed_url.geturl()))
|
||||||
|
|
||||||
|
for node in node_list:
|
||||||
|
self.known_nodes.add(node)
|
||||||
|
|
||||||
|
return node_list
|
||||||
|
|
||||||
|
def load_bootnodes(self,
|
||||||
|
read_storages: bool = True,
|
||||||
|
load_seed_nodes: bool = True,
|
||||||
|
retry_attempts: int = 3,
|
||||||
|
retry_rate: int = 2,
|
||||||
|
timeout=3):
|
||||||
|
"""
|
||||||
|
Engage known nodes from storages and pre-fetch hardcoded bootnode certificates for node learning.
|
||||||
|
"""
|
||||||
|
if load_seed_nodes is True:
|
||||||
|
socket.setdefaulttimeout(timeout) # Set Socket Timeout
|
||||||
|
|
||||||
|
unresponsive_seed_nodes = set()
|
||||||
|
|
||||||
|
def __attempt_bootnode_learning(bootnode, current_attempt=1):
|
||||||
|
self.log.debug("Loading Bootnode {}|{}".format(bootnode.checksum_address, bootnode.rest_url))
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.__learn_from_bootnode(bootnode=bootnode)
|
||||||
|
except socket.timeout:
|
||||||
|
if current_attempt == retry_attempts:
|
||||||
|
message = "No Response from Bootnode {} after {} attempts"
|
||||||
|
self.log.info(message.format(bootnode.rest_url, retry_attempts))
|
||||||
|
return
|
||||||
|
unresponsive_seed_nodes.add(bootnode)
|
||||||
|
self.log.info("No Response from Bootnode {}. Retrying in {} seconds...".format(bootnode.rest_url, retry_rate))
|
||||||
|
time.sleep(retry_rate)
|
||||||
|
__attempt_bootnode_learning(bootnode=bootnode, current_attempt=current_attempt+1)
|
||||||
|
else:
|
||||||
|
self.log.info("Successfully learned from bootnode {}".format(bootnode.rest_url))
|
||||||
|
if current_attempt > 1:
|
||||||
|
unresponsive_seed_nodes.remove(bootnode)
|
||||||
|
|
||||||
|
for bootnode in BOOTNODES:
|
||||||
|
__attempt_bootnode_learning(bootnode=bootnode)
|
||||||
|
if len(unresponsive_seed_nodes) > 0:
|
||||||
|
self.log.info("No Bootnodes were availible after {} attempts".format(retry_attempts))
|
||||||
|
|
||||||
|
if read_storages is True:
|
||||||
|
self.read_known_nodes()
|
||||||
|
|
Loading…
Reference in New Issue