mirror of https://github.com/nucypher/nucypher.git
Reorganize Ursula methods
parent
a59990f022
commit
0cbffe5afd
|
@ -581,15 +581,15 @@ class Character:
|
||||||
"Signature for message isn't valid: {}".format(signature_to_use))
|
"Signature for message isn't valid: {}".format(signature_to_use))
|
||||||
else:
|
else:
|
||||||
raise self.InvalidSignature("No signature provided -- signature presumed invalid.")
|
raise self.InvalidSignature("No signature provided -- signature presumed invalid.")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Next we have decrypt() and sign() - these use the private
|
||||||
|
# keys of their respective powers; any character who has these powers can use these functions.
|
||||||
|
#
|
||||||
|
# If they don't have the correct Power, the appropriate PowerUpError is raised.
|
||||||
|
#
|
||||||
return cleartext
|
return cleartext
|
||||||
|
|
||||||
"""
|
|
||||||
Next we have decrypt() and sign() - these use the private
|
|
||||||
keys of their respective powers; any character who has these powers can use these functions.
|
|
||||||
|
|
||||||
If they don't have the correct Power, the appropriate PowerUpError is raised.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def decrypt(self, message_kit, verifying_key: UmbralPublicKey = None):
|
def decrypt(self, message_kit, verifying_key: UmbralPublicKey = None):
|
||||||
return self._crypto_power.power_ups(EncryptingPower).decrypt(message_kit, verifying_key)
|
return self._crypto_power.power_ups(EncryptingPower).decrypt(message_kit, verifying_key)
|
||||||
|
|
||||||
|
@ -1012,7 +1012,9 @@ class Ursula(Character, VerifiableNode, ProxyRESTServer, Miner):
|
||||||
InterfaceInfo)
|
InterfaceInfo)
|
||||||
_dht_server_class = NucypherDHTServer
|
_dht_server_class = NucypherDHTServer
|
||||||
_alice_class = Alice
|
_alice_class = Alice
|
||||||
# TODO: Maybe this wants to be a registry, so that, for example, TLSHostingPower still can enjoy default status, but on a different class
|
|
||||||
|
# TODO: Maybe this wants to be a registry, so that, for example,
|
||||||
|
# TLSHostingPower still can enjoy default status, but on a different class
|
||||||
_default_crypto_powerups = [SigningPower, EncryptingPower]
|
_default_crypto_powerups = [SigningPower, EncryptingPower]
|
||||||
|
|
||||||
class NotFound(Exception):
|
class NotFound(Exception):
|
||||||
|
@ -1088,6 +1090,29 @@ class Ursula(Character, VerifiableNode, ProxyRESTServer, Miner):
|
||||||
if not federated_only:
|
if not federated_only:
|
||||||
self.substantiate_stamp()
|
self.substantiate_stamp()
|
||||||
|
|
||||||
|
def __bytes__(self):
|
||||||
|
|
||||||
|
import ipdb; ipdb.set_trace()
|
||||||
|
interface_info = VariableLengthBytestring(self.rest_interface)
|
||||||
|
|
||||||
|
if self.dht_interface:
|
||||||
|
interface_info += VariableLengthBytestring(self.dht_interface)
|
||||||
|
|
||||||
|
identity_evidence = VariableLengthBytestring(self._evidence_of_decentralized_identity)
|
||||||
|
|
||||||
|
as_bytes = bytes().join((bytes(self._interface_signature),
|
||||||
|
bytes(identity_evidence),
|
||||||
|
bytes(self.public_key(SigningPower)),
|
||||||
|
bytes(self.public_key(EncryptingPower)),
|
||||||
|
self.canonical_public_address,
|
||||||
|
interface_info)
|
||||||
|
)
|
||||||
|
return as_bytes
|
||||||
|
|
||||||
|
#
|
||||||
|
# Alternate Constructors
|
||||||
|
#
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_config(cls, filepath: str=DEFAULT_INI_FILEPATH, overrides: dict=None) -> 'Ursula':
|
def from_config(cls, filepath: str=DEFAULT_INI_FILEPATH, overrides: dict=None) -> 'Ursula':
|
||||||
"""
|
"""
|
||||||
|
@ -1102,157 +1127,6 @@ class Ursula(Character, VerifiableNode, ProxyRESTServer, Miner):
|
||||||
payload.update(overrides)
|
payload.update(overrides)
|
||||||
return cls(**payload)
|
return cls(**payload)
|
||||||
|
|
||||||
@only_me
|
|
||||||
def stake(self,
|
|
||||||
sample_rate: int = 10,
|
|
||||||
refresh_rate: int = 60,
|
|
||||||
confirm_now=True,
|
|
||||||
resume: bool = False,
|
|
||||||
expiration: maya.MayaDT = None,
|
|
||||||
lock_periods: int = None,
|
|
||||||
*args, **kwargs):
|
|
||||||
|
|
||||||
"""High-level staking daemon loop"""
|
|
||||||
|
|
||||||
if lock_periods and expiration:
|
|
||||||
raise ValueError("Pass the number of lock periods or an expiration MayaDT; not both.")
|
|
||||||
if expiration:
|
|
||||||
lock_periods = datetime_to_period(expiration)
|
|
||||||
|
|
||||||
if resume is False:
|
|
||||||
_staking_receipts = super().stake(expiration=expiration,
|
|
||||||
lock_periods=lock_periods,
|
|
||||||
*args, **kwargs)
|
|
||||||
|
|
||||||
# TODO: Check if this period has already been confirmed
|
|
||||||
# TODO: Check if there is an active stake in the current period: Resume staking daemon
|
|
||||||
# TODO: Validation and Sanity checks
|
|
||||||
|
|
||||||
if confirm_now:
|
|
||||||
self.confirm_activity()
|
|
||||||
|
|
||||||
# record start time and periods
|
|
||||||
start_time = maya.now()
|
|
||||||
uptime_period = self.miner_agent.get_current_period()
|
|
||||||
terminal_period = uptime_period + lock_periods
|
|
||||||
current_period = uptime_period
|
|
||||||
|
|
||||||
#
|
|
||||||
# Daemon
|
|
||||||
#
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
|
|
||||||
# calculate timedeltas
|
|
||||||
now = maya.now()
|
|
||||||
initialization_delta = now - start_time
|
|
||||||
|
|
||||||
# check if iteration re-samples
|
|
||||||
sample_stale = initialization_delta.seconds > (refresh_rate - 1)
|
|
||||||
if sample_stale:
|
|
||||||
|
|
||||||
period = self.miner_agent.get_current_period()
|
|
||||||
# check for stale sample data
|
|
||||||
if current_period != period:
|
|
||||||
|
|
||||||
# check for stake expiration
|
|
||||||
stake_expired = current_period >= terminal_period
|
|
||||||
if stake_expired:
|
|
||||||
break
|
|
||||||
|
|
||||||
self.confirm_activity()
|
|
||||||
current_period = period
|
|
||||||
|
|
||||||
# wait before resampling
|
|
||||||
time.sleep(sample_rate)
|
|
||||||
continue
|
|
||||||
|
|
||||||
finally:
|
|
||||||
|
|
||||||
# Cleanup #
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
@only_me
|
|
||||||
def stake(self,
|
|
||||||
sample_rate: int = 10,
|
|
||||||
refresh_rate: int = 60,
|
|
||||||
confirm_now=True,
|
|
||||||
resume: bool = False,
|
|
||||||
expiration: maya.MayaDT = None,
|
|
||||||
lock_periods: int = None,
|
|
||||||
*args, **kwargs):
|
|
||||||
|
|
||||||
"""High-level staking daemon loop"""
|
|
||||||
|
|
||||||
if lock_periods and expiration:
|
|
||||||
raise ValueError("Pass the number of lock periods or an expiration MayaDT; not both.")
|
|
||||||
if expiration:
|
|
||||||
lock_periods = datetime_to_period(expiration)
|
|
||||||
|
|
||||||
if resume is False:
|
|
||||||
_staking_receipts = super().stake(expiration=expiration,
|
|
||||||
lock_periods=lock_periods,
|
|
||||||
*args, **kwargs)
|
|
||||||
|
|
||||||
# TODO: Check if this period has already been confirmed
|
|
||||||
# TODO: Check if there is an active stake in the current period: Resume staking daemon
|
|
||||||
# TODO: Validation and Sanity checks
|
|
||||||
|
|
||||||
if confirm_now:
|
|
||||||
self.confirm_activity()
|
|
||||||
|
|
||||||
# record start time and periods
|
|
||||||
start_time = maya.now()
|
|
||||||
uptime_period = self.miner_agent.get_current_period()
|
|
||||||
terminal_period = uptime_period + lock_periods
|
|
||||||
current_period = uptime_period
|
|
||||||
|
|
||||||
#
|
|
||||||
# Daemon
|
|
||||||
#
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
|
|
||||||
# calculate timedeltas
|
|
||||||
now = maya.now()
|
|
||||||
initialization_delta = now - start_time
|
|
||||||
|
|
||||||
# check if iteration re-samples
|
|
||||||
sample_stale = initialization_delta.seconds > (refresh_rate - 1)
|
|
||||||
if sample_stale:
|
|
||||||
|
|
||||||
period = self.miner_agent.get_current_period()
|
|
||||||
# check for stale sample data
|
|
||||||
if current_period != period:
|
|
||||||
|
|
||||||
# check for stake expiration
|
|
||||||
stake_expired = current_period >= terminal_period
|
|
||||||
if stake_expired:
|
|
||||||
break
|
|
||||||
|
|
||||||
self.confirm_activity()
|
|
||||||
current_period = period
|
|
||||||
|
|
||||||
# wait before resampling
|
|
||||||
time.sleep(sample_rate)
|
|
||||||
continue
|
|
||||||
|
|
||||||
finally:
|
|
||||||
|
|
||||||
# Cleanup #
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rest_app(self):
|
|
||||||
if not self._rest_app:
|
|
||||||
m = "This Ursula doesn't have a REST app attached. If you want one, init with is_me and attach_server."
|
|
||||||
raise AttributeError(m)
|
|
||||||
else:
|
|
||||||
return self._rest_app
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_miner(cls, miner, *args, **kwargs):
|
def from_miner(cls, miner, *args, **kwargs):
|
||||||
|
@ -1276,49 +1150,6 @@ class Ursula(Character, VerifiableNode, ProxyRESTServer, Miner):
|
||||||
|
|
||||||
return stranger_ursula_from_public_keys
|
return stranger_ursula_from_public_keys
|
||||||
|
|
||||||
def attach_dht_server(self, ksize=20, alpha=3, id=None, storage=None, *args, **kwargs):
|
|
||||||
id = id or bytes(
|
|
||||||
self.canonical_public_address) # Ursula can still "mine" wallets until she gets a DHT ID she wants. Does that matter? #136
|
|
||||||
# TODO What do we actually want the node ID to be? Do we want to verify it somehow? 136
|
|
||||||
super().attach_dht_server(ksize=ksize, id=digest(id), alpha=alpha, storage=storage)
|
|
||||||
self.attach_rest_server()
|
|
||||||
|
|
||||||
def dht_listen(self):
|
|
||||||
if self.dht_interface is constants.NO_INTERFACE:
|
|
||||||
raise TypeError("This node does not have a DHT interface configured.")
|
|
||||||
return self.dht_server.listen(self.dht_interface.port,
|
|
||||||
self.dht_interface.host)
|
|
||||||
|
|
||||||
def interface_info_with_metadata(self):
|
|
||||||
# TODO: Do we ever actually use this without using the rest of the serialized Ursula? 337
|
|
||||||
|
|
||||||
return constants.BYTESTRING_IS_URSULA_IFACE_INFO + bytes(self)
|
|
||||||
|
|
||||||
def publish_dht_information(self):
|
|
||||||
# TODO: Simplify or wholesale deprecate this. 337
|
|
||||||
if not self.dht_interface:
|
|
||||||
raise RuntimeError("Must listen before publishing interface information.")
|
|
||||||
|
|
||||||
ursula_id = self.canonical_public_address
|
|
||||||
interface_value = self.interface_info_with_metadata()
|
|
||||||
setter = self.dht_server.set(key=ursula_id, value=interface_value)
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
loop.run_until_complete(setter)
|
|
||||||
return interface_value
|
|
||||||
|
|
||||||
def work_orders(self, bob=None):
|
|
||||||
"""
|
|
||||||
TODO: This is better written as a model method for Ursula's datastore.
|
|
||||||
"""
|
|
||||||
if not bob:
|
|
||||||
return self._work_orders
|
|
||||||
else:
|
|
||||||
work_orders_from_bob = []
|
|
||||||
for work_order in self._work_orders:
|
|
||||||
if work_order.bob == bob:
|
|
||||||
work_orders_from_bob.append(work_order)
|
|
||||||
return work_orders_from_bob
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bytes(cls, ursula_as_bytes, federated_only=False):
|
def from_bytes(cls, ursula_as_bytes, federated_only=False):
|
||||||
signature, identity_evidence, verifying_key, encrypting_key, public_address, rest_info, dht_info = cls._internal_splitter(
|
signature, identity_evidence, verifying_key, encrypting_key, public_address, rest_info, dht_info = cls._internal_splitter(
|
||||||
|
@ -1358,19 +1189,138 @@ class Ursula(Character, VerifiableNode, ProxyRESTServer, Miner):
|
||||||
|
|
||||||
return stranger_ursulas
|
return stranger_ursulas
|
||||||
|
|
||||||
def __bytes__(self):
|
#
|
||||||
interface_info = VariableLengthBytestring(self.rest_interface)
|
# Properties
|
||||||
|
#
|
||||||
|
|
||||||
if self.dht_interface:
|
@property
|
||||||
interface_info += VariableLengthBytestring(self.dht_interface)
|
def rest_app(self):
|
||||||
|
if not self._rest_app:
|
||||||
|
m = "This Ursula doesn't have a REST app attached. If you want one, init with is_me and attach_server."
|
||||||
|
raise AttributeError(m)
|
||||||
|
else:
|
||||||
|
return self._rest_app
|
||||||
|
|
||||||
identity_evidence = VariableLengthBytestring(self._evidence_of_decentralized_identity)
|
def interface_info_with_metadata(self):
|
||||||
|
# TODO: Do we ever actually use this without using the rest of the serialized Ursula? 337
|
||||||
|
|
||||||
as_bytes = bytes().join((bytes(self._interface_signature),
|
return constants.BYTESTRING_IS_URSULA_IFACE_INFO + bytes(self)
|
||||||
bytes(identity_evidence),
|
|
||||||
bytes(self.public_key(SigningPower)),
|
#
|
||||||
bytes(self.public_key(EncryptingPower)),
|
# Utilities
|
||||||
self.canonical_public_address,
|
#
|
||||||
interface_info)
|
|
||||||
)
|
def attach_dht_server(self,
|
||||||
return as_bytes
|
ksize: int =20,
|
||||||
|
alpha: int = 3,
|
||||||
|
id=None,
|
||||||
|
storage=None,
|
||||||
|
*args, **kwargs) -> None:
|
||||||
|
|
||||||
|
id = id or bytes(self.canonical_public_address) # Ursula can still "mine" wallets until she gets a DHT ID she wants. Does that matter? #136
|
||||||
|
# TODO What do we actually want the node ID to be? Do we want to verify it somehow? 136
|
||||||
|
super().attach_dht_server(ksize=ksize, id=digest(id), alpha=alpha, storage=storage)
|
||||||
|
self.attach_rest_server()
|
||||||
|
|
||||||
|
def dht_listen(self):
|
||||||
|
if self.dht_interface is constants.NO_INTERFACE:
|
||||||
|
raise TypeError("This node does not have a DHT interface configured.")
|
||||||
|
return self.dht_server.listen(self.dht_interface.port,
|
||||||
|
self.dht_interface.host)
|
||||||
|
|
||||||
|
def publish_dht_information(self):
|
||||||
|
# TODO: Simplify or wholesale deprecate this. 337
|
||||||
|
if not self.dht_interface:
|
||||||
|
raise RuntimeError("Must listen before publishing interface information.")
|
||||||
|
|
||||||
|
ursula_id = self.canonical_public_address
|
||||||
|
interface_value = self.interface_info_with_metadata()
|
||||||
|
setter = self.dht_server.set(key=ursula_id, value=interface_value)
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.run_until_complete(setter)
|
||||||
|
return interface_value
|
||||||
|
|
||||||
|
def work_orders(self, bob=None):
|
||||||
|
"""
|
||||||
|
TODO: This is better written as a model method for Ursula's datastore.
|
||||||
|
"""
|
||||||
|
if not bob:
|
||||||
|
return self._work_orders
|
||||||
|
else:
|
||||||
|
work_orders_from_bob = []
|
||||||
|
for work_order in self._work_orders:
|
||||||
|
if work_order.bob == bob:
|
||||||
|
work_orders_from_bob.append(work_order)
|
||||||
|
return work_orders_from_bob
|
||||||
|
|
||||||
|
@only_me
|
||||||
|
def stake(self,
|
||||||
|
sample_rate: int = 10,
|
||||||
|
refresh_rate: int = 60,
|
||||||
|
confirm_now=True,
|
||||||
|
resume: bool = False,
|
||||||
|
expiration: maya.MayaDT = None,
|
||||||
|
lock_periods: int = None,
|
||||||
|
*args, **kwargs) -> None:
|
||||||
|
|
||||||
|
"""High-level staking daemon loop"""
|
||||||
|
|
||||||
|
if lock_periods and expiration:
|
||||||
|
raise ValueError("Pass the number of lock periods or an expiration MayaDT; not both.")
|
||||||
|
if expiration:
|
||||||
|
lock_periods = datetime_to_period(expiration)
|
||||||
|
|
||||||
|
if resume is False:
|
||||||
|
_staking_receipts = super().initialize_stake(expiration=expiration,
|
||||||
|
lock_periods=lock_periods,
|
||||||
|
*args, **kwargs)
|
||||||
|
|
||||||
|
# TODO: Check if this period has already been confirmed
|
||||||
|
# TODO: Check if there is an active stake in the current period: Resume staking daemon
|
||||||
|
# TODO: Validation and Sanity checks
|
||||||
|
|
||||||
|
if confirm_now:
|
||||||
|
self.confirm_activity()
|
||||||
|
|
||||||
|
# record start time and periods
|
||||||
|
start_time = maya.now()
|
||||||
|
uptime_period = self.miner_agent.get_current_period()
|
||||||
|
terminal_period = uptime_period + lock_periods
|
||||||
|
current_period = uptime_period
|
||||||
|
|
||||||
|
#
|
||||||
|
# Daemon
|
||||||
|
#
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
|
||||||
|
# calculate timedeltas
|
||||||
|
now = maya.now()
|
||||||
|
initialization_delta = now - start_time
|
||||||
|
|
||||||
|
# check if iteration re-samples
|
||||||
|
sample_stale = initialization_delta.seconds > (refresh_rate - 1)
|
||||||
|
if sample_stale:
|
||||||
|
|
||||||
|
period = self.miner_agent.get_current_period()
|
||||||
|
# check for stale sample data
|
||||||
|
if current_period != period:
|
||||||
|
|
||||||
|
# check for stake expiration
|
||||||
|
stake_expired = current_period >= terminal_period
|
||||||
|
if stake_expired:
|
||||||
|
break
|
||||||
|
|
||||||
|
self.confirm_activity()
|
||||||
|
current_period = period
|
||||||
|
|
||||||
|
# wait before resampling
|
||||||
|
time.sleep(sample_rate)
|
||||||
|
continue
|
||||||
|
|
||||||
|
finally:
|
||||||
|
|
||||||
|
# TODO: Cleanup #
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
Loading…
Reference in New Issue