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') click.secho("Running in {} mode".format(config.operating_mode), fg='blue')
# Bootnodes, Seeds, Known Nodes # Bootnodes, Seeds, Known Nodes
ursula_config.get_bootnodes() ursula_config.load_bootnodes()
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'))

View File

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

View File

@ -398,7 +398,6 @@ class Ursula(Character, VerifiableNode, Miner):
rest_port: int, rest_port: int,
certificate: Certificate = None, certificate: Certificate = None,
certificate_filepath: str = None, certificate_filepath: str = None,
tls_private_key = None, # TODO: Derive from keyring
db_name: str = None, db_name: str = None,
db_filepath: str = None, db_filepath: str = None,
@ -442,31 +441,32 @@ class Ursula(Character, VerifiableNode, Miner):
if is_me is True: # TODO: 340 if is_me is True: # TODO: 340
self._stored_treasure_maps = dict() self._stored_treasure_maps = dict()
#
# Staking Ursula
#
if not federated_only: if not federated_only:
Miner.__init__(self, is_me=is_me, miner_agent=miner_agent, checksum_address=checksum_address) 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) blockchain_power = BlockchainPower(blockchain=self.blockchain, account=self.checksum_public_address)
self._crypto_power.consume_power_up(blockchain_power) 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) 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): 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: if is_me:
self.suspicious_activities_witnessed = {'vladimirs': [], 'bad_treasure_maps': []} self.suspicious_activities_witnessed = {'vladimirs': [], 'bad_treasure_maps': []}
# #
# REST Server # REST Server (Ephemeral Self-Ursula)
# #
rest_routes = ProxyRESTRoutes( rest_routes = ProxyRESTRoutes(
db_name=db_name, db_name=db_name,
@ -484,62 +484,41 @@ class Ursula(Character, VerifiableNode, Miner):
certificate_dir=self.known_certificates_dir, 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( tls_hosting_keypair = HostingKeypair(curve=tls_curve, host=rest_host)
curve=tls_curve, tls_hosting_power = TLSHostingPower(keypair=tls_hosting_keypair, host=rest_host)
host=rest_host, self.rest_server = ProxyRESTServer(rest_host=rest_host, rest_port=rest_port,
certificate=certificate, routes=rest_routes, hosting_power=tls_hosting_power)
certificate_filepath=certificate_filepath,
private_key=tls_private_key
)
tls_hosting_power = TLSHostingPower(rest_server=rest_server,
keypair=tls_hosting_keypair)
# #
# Stranger-Ursula # Stranger-Ursula
# #
else: 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 # REST Server
# Unless the caller passed a crypto power, # Unless the caller passed a crypto power we'll make our own TLSHostingPower for this stranger.
# we'll make our own TLSHostingPower for this stranger. self.rest_server = ProxyRESTServer(
rest_server = ProxyRESTServer(
rest_host=rest_host, 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 # OK - Now we have a ProxyRestServer and a TLSHostingPower for some Ursula
#
self._crypto_power.consume_power_up(tls_hosting_power) # Consume! 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 # Verifiable Node
# #
@ -565,7 +544,7 @@ class Ursula(Character, VerifiableNode, Miner):
hosting_power = self._crypto_power.power_ups(TLSHostingPower) hosting_power = self._crypto_power.power_ups(TLSHostingPower)
return ( return (
hosting_power.rest_server.rest_interface, self.rest_server.rest_interface,
hosting_power.keypair.certificate, hosting_power.keypair.certificate,
hosting_power.keypair.pubkey hosting_power.keypair.pubkey
) )
@ -608,7 +587,7 @@ class Ursula(Character, VerifiableNode, Miner):
port: int, port: int,
federated_only: bool = False) -> 'Ursula': 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: if not response.status_code == 200:
raise RuntimeError("Got a bad response: {}".format(response)) raise RuntimeError("Got a bad response: {}".format(response))
@ -686,16 +665,30 @@ class Ursula(Character, VerifiableNode, Miner):
def from_storage(cls, def from_storage(cls,
node_storage: NodeStorage, node_storage: NodeStorage,
checksum_adress: str, checksum_adress: str,
federated_only: bool = False, *args, **kwargs) -> 'Ursula': federated_only: bool = False) -> 'Ursula':
return node_storage.get(checksum_address=checksum_adress) return node_storage.get(checksum_address=checksum_adress,
federated_only=federated_only)
# #
# Properties # 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 @property
def rest_app(self): 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: 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." 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
tls_curve: EllipticCurve = None, tls_curve: EllipticCurve = None,
certificate: Certificate = None, certificate: Certificate = None,
tls_private_key: bytes = None, # TODO: from config here
certificate_filepath: str = None, certificate_filepath: str = None,
# Ursula # Ursula
@ -63,7 +62,6 @@ class UrsulaConfiguration(NodeConfiguration):
self.tls_curve = tls_curve or self.__DEFAULT_TLS_CURVE self.tls_curve = tls_curve or self.__DEFAULT_TLS_CURVE
self.certificate = certificate self.certificate = certificate
self.certificate_filepath = certificate_filepath self.certificate_filepath = certificate_filepath
self.tls_private_key = tls_private_key
# Ursula # Ursula
self.interface_signature = interface_signature self.interface_signature = interface_signature
@ -114,9 +112,6 @@ class UrsulaConfiguration(NodeConfiguration):
timestamp=None, timestamp=None,
miner_agent=self.miner_agent 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} return {**super().dynamic_payload, **payload}
def produce(self, passphrase: str = None, **overrides): def produce(self, passphrase: str = None, **overrides):

View File

@ -1,4 +1,3 @@
import os
from collections import namedtuple from collections import namedtuple
from os.path import abspath, dirname from os.path import abspath, dirname
@ -12,12 +11,11 @@ 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 # Bootnodes
#
Bootnode = namedtuple('Bootnode', ['checksum_address', 'rest_url']) Bootnode = namedtuple('Bootnode', ['checksum_address', 'rest_url'])
BOOTNODES = ( BOOTNODES = (
Bootnode('0xDbf2Bc4b81eB46CdDfa52348Ecf3c142841267E0', 'https://18.223.117.103:9151'), 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.config.constants import DEFAULT_CONFIG_ROOT
from nucypher.crypto.api import generate_self_signed_certificate from nucypher.crypto.api import generate_self_signed_certificate
from nucypher.crypto.powers import SigningPower, EncryptingPower, KeyPairBasedPower, DerivedKeyBasedPower from nucypher.crypto.powers import SigningPower, EncryptingPower, KeyPairBasedPower, DerivedKeyBasedPower
from nucypher.network.server import TLSHostingPower
FILE_ENCODING = 'utf-8' FILE_ENCODING = 'utf-8'
@ -388,19 +389,6 @@ class NucypherKeyring:
def __del__(self) -> None: def __del__(self) -> None:
self.lock() 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 # Public Keys
# #
@ -520,10 +508,10 @@ class NucypherKeyring:
if issubclass(power_class, KeyPairBasedPower): if issubclass(power_class, KeyPairBasedPower):
codex = {SigningPower: self.__signing_keypath, codex = {SigningPower: self.__signing_keypath,
EncryptingPower: self.__root_keypath EncryptingPower: self.__root_keypath,
TLSHostingPower: self.__tls_keypath, # TODO
# BlockchainPower: self.__wallet_path, # TODO # BlockchainPower: self.__wallet_path, # TODO
# TLSHostingPower: self.__tls_keypath} # TODO }
}
# Create Power # Create Power
try: try:

View File

@ -18,12 +18,11 @@ from cryptography.hazmat.primitives.serialization import Encoding
from nucypher.characters.lawful import Ursula from nucypher.characters.lawful import Ursula
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, BASE_DIR, BOOTNODES 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.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.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:
@ -395,7 +394,7 @@ class NodeConfiguration:
curve=tls_curve, curve=tls_curve,
keyring_root=self.keyring_dir) keyring_root=self.keyring_dir)
# TODO: Operating mode switch # TODO: Operating mode switch #466
if self.federated_only or not wallet: if self.federated_only or not wallet:
self.checksum_address = self.keyring.federated_address self.checksum_address = self.keyring.federated_address
else: else:

View File

@ -24,8 +24,11 @@ class ProxyRESTServer:
def __init__(self, def __init__(self,
rest_host: str, rest_host: str,
rest_port: int, rest_port: int,
hosting_power = None,
routes: 'ProxyRESTRoutes' = None, routes: 'ProxyRESTRoutes' = None,
) -> None: ) -> None:
self.routes = routes
self.rest_interface = InterfaceInfo(host=rest_host, port=rest_port) self.rest_interface = InterfaceInfo(host=rest_host, port=rest_port)
if routes: # if is me if routes: # if is me
self.rest_app = routes.rest_app self.rest_app = routes.rest_app
@ -33,6 +36,8 @@ class ProxyRESTServer:
else: else:
self.rest_app = constants.PUBLIC_ONLY self.rest_app = constants.PUBLIC_ONLY
self.__hosting_power = hosting_power
def rest_url(self): def rest_url(self):
return "{}:{}".format(self.rest_interface.host, self.rest_interface.port) return "{}:{}".format(self.rest_interface.host, self.rest_interface.port)
@ -291,18 +296,17 @@ class TLSHostingPower(KeyPairBasedPower):
not_found_error = NoHostingPower not_found_error = NoHostingPower
def __init__(self, def __init__(self,
rest_server, host: str,
certificate=None, public_certificate=None,
certificate_filepath=None, public_certificate_filepath=None,
*args, **kwargs) -> 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? # 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: if public_certificate:
kwargs['keypair'] = HostingKeypair(certificate=certificate, host=rest_server.rest_interface.host) kwargs['keypair'] = HostingKeypair(certificate=public_certificate, host=host)
elif certificate_filepath: elif public_certificate_filepath:
kwargs['keypair'] = HostingKeypair(certificate_filepath=certificate_filepath) kwargs['keypair'] = HostingKeypair(certificate_filepath=public_certificate_filepath, host=host)
self.rest_server = rest_server
super().__init__(*args, **kwargs) 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.registry import TemporaryEthereumContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.config.characters import UrsulaConfiguration, AliceConfiguration, BobConfiguration 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.config.node import NodeConfiguration
from nucypher.data_sources import DataSource from nucypher.data_sources import DataSource
from nucypher.keystore import keystore 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.middleware import MockRestMiddleware
from nucypher.utilities.sandbox.ursula import make_federated_ursulas, make_decentralized_ursulas 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 # Temporary