Derive HostingPower from Keyring

pull/480/head
Kieran Prasch 2018-10-10 20:11:25 -07:00
parent 4131c8ca0c
commit 5f84157b96
9 changed files with 74 additions and 95 deletions

View File

@ -1170,7 +1170,7 @@ def ursula(config,
click.secho("Running in {} mode".format(config.operating_mode), fg='blue')
# Bootnodes, Seeds, Known Nodes
ursula_config.get_bootnodes()
ursula_config.load_bootnodes()
quantity_known_nodes = len(ursula_config.known_nodes)
if quantity_known_nodes > 0:
click.secho("Loaded {} known nodes from storages".format(quantity_known_nodes, fg='blue'))

View File

@ -574,7 +574,7 @@ class Character(Learner):
@property
def rest_interface(self):
return self._crypto_power.power_ups(TLSHostingPower).rest_server.rest_url()
return self.rest_server.rest_url()
@property
def stamp(self):

View File

@ -398,7 +398,6 @@ class Ursula(Character, VerifiableNode, Miner):
rest_port: int,
certificate: Certificate = None,
certificate_filepath: str = None,
tls_private_key = None, # TODO: Derive from keyring
db_name: str = None,
db_filepath: str = None,
@ -442,31 +441,32 @@ class Ursula(Character, VerifiableNode, Miner):
if is_me is True: # TODO: 340
self._stored_treasure_maps = dict()
#
# Staking Ursula
#
if not federated_only:
Miner.__init__(self, is_me=is_me, miner_agent=miner_agent, checksum_address=checksum_address)
# Access staking node via node's transacting keys
# Access staking node via node's transacting keys TODO: Better handle ephemeral staking self ursula
blockchain_power = BlockchainPower(blockchain=self.blockchain, account=self.checksum_public_address)
self._crypto_power.consume_power_up(blockchain_power)
# Use blockchain power to substantiate stamp
# Use blockchain power to substantiate stamp, instead of signing key
self.substantiate_stamp(passphrase=passphrase)
#
# ProxyRESTServer and TLSHostingPower
# ProxyRESTServer and TLSHostingPower # TODO: Maybe we want _power_ups to be public after all?
#
if not crypto_power or (TLSHostingPower not in crypto_power._power_ups):
# TODO: Maybe we want _power_ups to be public after all?
# We'll hook all the TLS stuff up unless the crypto_power was already passed.
#
# Self-Ursula
# Ephemeral Self-Ursula
#
if is_me:
self.suspicious_activities_witnessed = {'vladimirs': [], 'bad_treasure_maps': []}
#
# REST Server
# REST Server (Ephemeral Self-Ursula)
#
rest_routes = ProxyRESTRoutes(
db_name=db_name,
@ -484,62 +484,41 @@ class Ursula(Character, VerifiableNode, Miner):
certificate_dir=self.known_certificates_dir,
)
rest_server = ProxyRESTServer(
rest_host=rest_host,
rest_port=rest_port,
routes=rest_routes,
)
self.rest_url = rest_server.rest_url
self.datastore = rest_routes.datastore # TODO: Maybe organize this better?
#
# TLSHostingPower
# TLSHostingPower (Ephemeral Self-Ursula)
#
tls_hosting_keypair = HostingKeypair(
curve=tls_curve,
host=rest_host,
certificate=certificate,
certificate_filepath=certificate_filepath,
private_key=tls_private_key
)
tls_hosting_power = TLSHostingPower(rest_server=rest_server,
keypair=tls_hosting_keypair)
tls_hosting_keypair = HostingKeypair(curve=tls_curve, host=rest_host)
tls_hosting_power = TLSHostingPower(keypair=tls_hosting_keypair, host=rest_host)
self.rest_server = ProxyRESTServer(rest_host=rest_host, rest_port=rest_port,
routes=rest_routes, hosting_power=tls_hosting_power)
#
# Stranger-Ursula
#
else:
# TLSHostingPower
if certificate or certificate_filepath:
tls_hosting_power = TLSHostingPower(host=rest_host,
public_certificate_filepath=certificate_filepath,
public_certificate=certificate)
else:
tls_hosting_keypair = HostingKeypair(curve=tls_curve, host=rest_host)
tls_hosting_power = TLSHostingPower(host=rest_host, keypair=tls_hosting_keypair)
# REST Server
# Unless the caller passed a crypto power,
# we'll make our own TLSHostingPower for this stranger.
rest_server = ProxyRESTServer(
# Unless the caller passed a crypto power we'll make our own TLSHostingPower for this stranger.
self.rest_server = ProxyRESTServer(
rest_host=rest_host,
rest_port=rest_port
rest_port=rest_port,
hosting_power=tls_hosting_power
)
#
# TLSHostingPower
#
if certificate or certificate_filepath:
tls_hosting_power = TLSHostingPower(rest_server=rest_server,
certificate_filepath=certificate_filepath,
certificate=certificate)
else:
tls_hosting_keypair = HostingKeypair(
curve=tls_curve,
host=rest_host,
certificate_filepath=certificate_filepath)
tls_hosting_power = TLSHostingPower(rest_server=rest_server,
keypair=tls_hosting_keypair)
#
# OK - Now we have a ProxyRestServer and a TLSHostingPower for some Ursula
#
self._crypto_power.consume_power_up(tls_hosting_power) # Consume!
else:
self.log.info("Not adhering rest_server; Using the one on crypto_power.")
#
# Verifiable Node
#
@ -565,7 +544,7 @@ class Ursula(Character, VerifiableNode, Miner):
hosting_power = self._crypto_power.power_ups(TLSHostingPower)
return (
hosting_power.rest_server.rest_interface,
self.rest_server.rest_interface,
hosting_power.keypair.certificate,
hosting_power.keypair.pubkey
)
@ -608,7 +587,7 @@ class Ursula(Character, VerifiableNode, Miner):
port: int,
federated_only: bool = False) -> 'Ursula':
response = network_middleware.node_information(host, port) # TODO
response = network_middleware.node_information(host, port) # TODO: pre-load certificates here?
if not response.status_code == 200:
raise RuntimeError("Got a bad response: {}".format(response))
@ -686,16 +665,30 @@ class Ursula(Character, VerifiableNode, Miner):
def from_storage(cls,
node_storage: NodeStorage,
checksum_adress: str,
federated_only: bool = False, *args, **kwargs) -> 'Ursula':
return node_storage.get(checksum_address=checksum_adress)
federated_only: bool = False) -> 'Ursula':
return node_storage.get(checksum_address=checksum_adress,
federated_only=federated_only)
#
# Properties
#
@property
def datastore(self):
try:
return self.rest_server.routes.datastore
except AttributeError:
raise AttributeError("No rest server attached")
@property
def rest_url(self):
try:
return self.rest_server.rest_url
except AttributeError:
raise AttributeError("No rest server attached")
@property
def rest_app(self):
rest_app_on_server = self._crypto_power.power_ups(TLSHostingPower).rest_server.rest_app
rest_app_on_server = self.rest_server.rest_app
if not rest_app_on_server:
m = "This Ursula doesn't have a REST app attached. If you want one, init with is_me and attach_server."

View File

@ -34,7 +34,6 @@ class UrsulaConfiguration(NodeConfiguration):
# TLS
tls_curve: EllipticCurve = None,
certificate: Certificate = None,
tls_private_key: bytes = None, # TODO: from config here
certificate_filepath: str = None,
# Ursula
@ -63,7 +62,6 @@ class UrsulaConfiguration(NodeConfiguration):
self.tls_curve = tls_curve or self.__DEFAULT_TLS_CURVE
self.certificate = certificate
self.certificate_filepath = certificate_filepath
self.tls_private_key = tls_private_key
# Ursula
self.interface_signature = interface_signature
@ -114,9 +112,6 @@ class UrsulaConfiguration(NodeConfiguration):
timestamp=None,
miner_agent=self.miner_agent
)
if not self.temp:
tls_private_key = self.keyring.tls_private_key
payload.update(dict(tls_private_key=tls_private_key))
return {**super().dynamic_payload, **payload}
def produce(self, passphrase: str = None, **overrides):

View File

@ -1,4 +1,3 @@
import os
from collections import namedtuple
from os.path import abspath, dirname
@ -12,12 +11,11 @@ PROJECT_ROOT = abspath(dirname(nucypher.__file__))
APP_DIR = AppDirs("nucypher", "NuCypher")
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_CONTRACTS_DIR = os.path.join(BASE_DIR, 'tests', 'blockchain', 'eth', 'contracts', 'contracts')
NUMBER_OF_URSULAS_IN_MOCK_NETWORK = 10

View File

@ -25,6 +25,7 @@ from umbral.keys import UmbralPrivateKey, UmbralPublicKey
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
from nucypher.crypto.api import generate_self_signed_certificate
from nucypher.crypto.powers import SigningPower, EncryptingPower, KeyPairBasedPower, DerivedKeyBasedPower
from nucypher.network.server import TLSHostingPower
FILE_ENCODING = 'utf-8'
@ -388,19 +389,6 @@ class NucypherKeyring:
def __del__(self) -> None:
self.lock()
#
# Private Keys
#
@property
@unlock_required
def tls_private_key(self):
"""TODO: Deprecate and use self.derive_crypto_power instead"""
key_data = _read_keyfile(keypath=self.__tls_keypath, deserializer=None)
__tls_key = serialization.load_pem_private_key(data=key_data,
password=self.__derived_key_material,
backend=default_backend())
return __tls_key
#
# Public Keys
#
@ -520,10 +508,10 @@ class NucypherKeyring:
if issubclass(power_class, KeyPairBasedPower):
codex = {SigningPower: self.__signing_keypath,
EncryptingPower: self.__root_keypath
EncryptingPower: self.__root_keypath,
TLSHostingPower: self.__tls_keypath, # TODO
# BlockchainPower: self.__wallet_path, # TODO
# TLSHostingPower: self.__tls_keypath} # TODO
}
}
# Create Power
try:

View File

@ -18,12 +18,11 @@ from cryptography.hazmat.primitives.serialization import Encoding
from nucypher.characters.lawful import Ursula
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.keyring import NucypherKeyring, _write_tls_certificate
from nucypher.config.storages import NodeStorage, InMemoryNodeStorage
from nucypher.crypto.powers import CryptoPowerUp
from nucypher.crypto.signing import signature_splitter
from nucypher.network.middleware import RestMiddleware
from nucypher.network.nodes import VerifiableNode
class NodeConfiguration:
@ -395,7 +394,7 @@ class NodeConfiguration:
curve=tls_curve,
keyring_root=self.keyring_dir)
# TODO: Operating mode switch
# TODO: Operating mode switch #466
if self.federated_only or not wallet:
self.checksum_address = self.keyring.federated_address
else:

View File

@ -24,8 +24,11 @@ class ProxyRESTServer:
def __init__(self,
rest_host: str,
rest_port: int,
hosting_power = None,
routes: 'ProxyRESTRoutes' = None,
) -> None:
self.routes = routes
self.rest_interface = InterfaceInfo(host=rest_host, port=rest_port)
if routes: # if is me
self.rest_app = routes.rest_app
@ -33,6 +36,8 @@ class ProxyRESTServer:
else:
self.rest_app = constants.PUBLIC_ONLY
self.__hosting_power = hosting_power
def rest_url(self):
return "{}:{}".format(self.rest_interface.host, self.rest_interface.port)
@ -291,18 +296,17 @@ class TLSHostingPower(KeyPairBasedPower):
not_found_error = NoHostingPower
def __init__(self,
rest_server,
certificate=None,
certificate_filepath=None,
host: str,
public_certificate=None,
public_certificate_filepath=None,
*args, **kwargs) -> None:
if certificate and certificate_filepath:
if public_certificate and public_certificate_filepath:
# TODO: Design decision here: if they do pass both, and they're identical, do we let that slide?
raise ValueError("Pass either a certificate or a certificate_filepath, not both.")
raise ValueError("Pass either a public_certificate or a public_certificate_filepath, not both.")
if certificate:
kwargs['keypair'] = HostingKeypair(certificate=certificate, host=rest_server.rest_interface.host)
elif certificate_filepath:
kwargs['keypair'] = HostingKeypair(certificate_filepath=certificate_filepath)
self.rest_server = rest_server
if public_certificate:
kwargs['keypair'] = HostingKeypair(certificate=public_certificate, host=host)
elif public_certificate_filepath:
kwargs['keypair'] = HostingKeypair(certificate_filepath=public_certificate_filepath, host=host)
super().__init__(*args, **kwargs)

View File

@ -16,7 +16,7 @@ from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface
from nucypher.blockchain.eth.registry import TemporaryEthereumContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.config.characters import UrsulaConfiguration, AliceConfiguration, BobConfiguration
from nucypher.config.constants import TEST_CONTRACTS_DIR
from nucypher.config.constants import BASE_DIR
from nucypher.config.node import NodeConfiguration
from nucypher.data_sources import DataSource
from nucypher.keystore import keystore
@ -29,6 +29,8 @@ from nucypher.utilities.sandbox.constants import (DEFAULT_NUMBER_OF_URSULAS_IN_D
from nucypher.utilities.sandbox.middleware import MockRestMiddleware
from nucypher.utilities.sandbox.ursula import make_federated_ursulas, make_decentralized_ursulas
TEST_CONTRACTS_DIR = os.path.join(BASE_DIR, 'tests', 'blockchain', 'eth', 'contracts', 'contracts')
#
# Temporary