From a4de8a882edffdad33e5168251606255ae498c93 Mon Sep 17 00:00:00 2001 From: Owen Campbell Date: Tue, 27 Jun 2023 12:41:53 +0100 Subject: [PATCH] Make the use of string literal type hints compatible with PEP 484 Because: - Not doing so causes F821 linting errors https://peps.python.org/pep-0484/#forward-references https://www.flake8rules.com/rules/F821.html --- nucypher/acumen/perception.py | 64 ++++++++------ nucypher/blockchain/eth/actors.py | 4 +- nucypher/blockchain/eth/agents.py | 98 +++++++++++++--------- nucypher/blockchain/eth/events.py | 17 ++-- nucypher/blockchain/eth/trackers/dkg.py | 3 +- nucypher/characters/lawful.py | 21 ++--- nucypher/network/middleware.py | 17 +++- nucypher/network/nodes.py | 43 +++++----- nucypher/network/retrieval.py | 16 ++-- nucypher/network/trackers.py | 7 +- nucypher/policy/payment.py | 8 +- nucypher/policy/policies.py | 79 +++++++++-------- nucypher/types.py | 8 +- nucypher/utilities/prometheus/collector.py | 8 +- nucypher/utilities/prometheus/metrics.py | 11 ++- tests/mock/agents.py | 3 +- 16 files changed, 237 insertions(+), 170 deletions(-) diff --git a/nucypher/acumen/perception.py b/nucypher/acumen/perception.py index c742d3029..41c8c9389 100644 --- a/nucypher/acumen/perception.py +++ b/nucypher/acumen/perception.py @@ -11,6 +11,7 @@ import maya from eth_typing import ChecksumAddress from nucypher_core import FleetStateChecksum, NodeMetadata +from nucypher import characters from nucypher.utilities.logging import Logger from .nicknames import Nickname @@ -41,7 +42,7 @@ class StateDiff(NamedTuple): class FleetState: """ - Fleet state as perceived by a local Ursula. + Fleet state as perceived by a local "lawful.Ursula". Assumptions we're based on: @@ -57,7 +58,9 @@ class FleetState: """ @classmethod - def new(cls, this_node: Optional['Ursula'] = None) -> 'FleetState': + def new( + cls, this_node: Optional["characters.lawful.Ursula"] = None + ) -> "FleetState": this_node_ref = weakref.ref(this_node) if this_node else None # `this_node` might not have its metadata available yet. this_node_metadata = None @@ -66,12 +69,16 @@ class FleetState: this_node_ref=this_node_ref, this_node_metadata=this_node_metadata) - def __init__(self, - nodes: Dict[ChecksumAddress, 'Ursula'], - this_node_ref: Optional[weakref.ReferenceType], - this_node_metadata: Optional[NodeMetadata]): - self.checksum = FleetStateChecksum(this_node=this_node_metadata, - other_nodes=[node.metadata() for node in nodes.values()]) + def __init__( + self, + nodes: Dict[ChecksumAddress, "characters.lawful.Ursula"], + this_node_ref: Optional[weakref.ReferenceType], + this_node_metadata: Optional[NodeMetadata], + ): + self.checksum = FleetStateChecksum( + this_node=this_node_metadata, + other_nodes=[node.metadata() for node in nodes.values()], + ) self.nickname = Nickname.from_seed(bytes(self.checksum), length=1) self._nodes = nodes self.timestamp = maya.now() @@ -84,12 +91,12 @@ class FleetState: timestamp=self.timestamp, population=self.population) - def _calculate_diff(self, - this_node_updated: bool, - nodes_to_add: Iterable['Ursula'], - nodes_to_remove: Iterable[ChecksumAddress] - ) -> StateDiff: - + def _calculate_diff( + self, + this_node_updated: bool, + nodes_to_add: Iterable["characters.lawful.Ursula"], + nodes_to_remove: Iterable[ChecksumAddress], + ) -> StateDiff: nodes_updated = [] for node in nodes_to_add: if node.checksum_address in nodes_to_remove: @@ -107,12 +114,12 @@ class FleetState: nodes_updated=nodes_updated, nodes_removed=nodes_removed) - def with_updated_nodes(self, - nodes_to_add: Iterable['Ursula'], - nodes_to_remove: Iterable[ChecksumAddress], - skip_this_node: bool = False, - ) -> 'FleetState': - + def with_updated_nodes( + self, + nodes_to_add: Iterable["characters.lawful.Ursula"], + nodes_to_remove: Iterable[ChecksumAddress], + skip_this_node: bool = False, + ) -> "FleetState": if self._this_node_ref is not None and not skip_this_node: this_node = self._this_node_ref() this_node_metadata = this_node.metadata() @@ -169,7 +176,7 @@ class FleetState: def __len__(self): return len(self._nodes) - def shuffled(self) -> List['Ursula']: + def shuffled(self) -> List["characters.lawful.Ursula"]: nodes_we_know_about = list(self._nodes.values()) random.shuffle(nodes_we_know_about) return nodes_we_know_about @@ -206,8 +213,9 @@ class FleetSensor: """ log = Logger("Learning") - def __init__(self, domain: str, this_node: Optional['Ursula'] = None): - + def __init__( + self, domain: str, this_node: Optional["characters.lawful.Ursula"] = None + ): self._domain = domain self._current_state = FleetState.new(this_node) @@ -221,7 +229,7 @@ class FleetSensor: self._auto_update_state = False - def record_node(self, node: 'Ursula'): + def record_node(self, node: "characters.lawful.Ursula"): if node.domain == self._domain: # Replace the existing object with a newer object, even if they're equal @@ -330,7 +338,7 @@ class FleetSensor: def shuffled(self): return self._current_state.shuffled() - def mark_as(self, label: Exception, node: 'Ursula'): + def mark_as(self, label: Exception, node: "characters.lawful.Ursula"): # TODO: for now we're not using `label` in any way, so we're just ignoring it self._nodes_to_remove.add(node.checksum_address) @@ -352,8 +360,10 @@ class FleetSensor: self._remote_last_seen[checksum_address] = maya.now() self._remote_states[checksum_address] = state - def status_info(self, checksum_address_or_node: Union[ChecksumAddress, 'Ursula']) -> 'RemoteUrsulaStatus': - + def status_info( + self, + checksum_address_or_node: Union[ChecksumAddress, "characters.lawful.Ursula"], + ) -> "RemoteUrsulaStatus": if isinstance(checksum_address_or_node, str): node = self[checksum_address_or_node] else: diff --git a/nucypher/blockchain/eth/actors.py b/nucypher/blockchain/eth/actors.py index b23d6fe36..216b18510 100644 --- a/nucypher/blockchain/eth/actors.py +++ b/nucypher/blockchain/eth/actors.py @@ -39,7 +39,7 @@ from nucypher.blockchain.eth.registry import ( ) from nucypher.blockchain.eth.signers import Signer from nucypher.blockchain.eth.token import NU -from nucypher.blockchain.eth.trackers.dkg import ActiveRitualTracker +from nucypher.blockchain.eth.trackers import dkg from nucypher.blockchain.eth.trackers.pre import WorkTracker from nucypher.crypto.ferveo.dkg import DecryptionShareSimple, FerveoVariant, Transcript from nucypher.crypto.powers import ( @@ -319,7 +319,7 @@ class Ritualist(BaseActor): ) # track active onchain rituals - self.ritual_tracker = ActiveRitualTracker( + self.ritual_tracker = dkg.ActiveRitualTracker( ritualist=self, ) diff --git a/nucypher/blockchain/eth/agents.py b/nucypher/blockchain/eth/agents.py index b5628aa1a..c3549a467 100644 --- a/nucypher/blockchain/eth/agents.py +++ b/nucypher/blockchain/eth/agents.py @@ -15,6 +15,8 @@ from nucypher_core.ferveo import AggregatedTranscript, DkgPublicKey, Transcript from web3.contract.contract import Contract, ContractFunction from web3.types import Timestamp, TxParams, TxReceipt, Wei +from nucypher import types +from nucypher.blockchain.eth import events from nucypher.blockchain.eth.constants import ( ADJUDICATOR_CONTRACT_NAME, DISPATCHER_CONTRACT_NAME, @@ -25,7 +27,6 @@ from nucypher.blockchain.eth.constants import ( SUBSCRIPTION_MANAGER_CONTRACT_NAME, ) from nucypher.blockchain.eth.decorators import contract_api -from nucypher.blockchain.eth.events import ContractEvents from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory from nucypher.blockchain.eth.registry import BaseContractRegistry from nucypher.config.constants import ( @@ -34,7 +35,6 @@ from nucypher.config.constants import ( ) from nucypher.crypto.powers import TransactingPower from nucypher.crypto.utils import sha256_digest -from nucypher.types import Agent, NuNits, StakingProviderInfo, TuNits from nucypher.utilities.logging import Logger # type: ignore @@ -87,7 +87,7 @@ class EthereumContractAgent: ) self.__contract = contract - self.events = ContractEvents(contract) + self.events = events.ContractEvents(contract) if not transaction_gas: transaction_gas = EthereumContractAgent.DEFAULT_TRANSACTION_GAS_LIMITS['default'] self.transaction_gas = transaction_gas @@ -129,23 +129,26 @@ class NucypherTokenAgent(EthereumContractAgent): contract_name: str = NUCYPHER_TOKEN_CONTRACT_NAME @contract_api(CONTRACT_CALL) - def get_balance(self, address: ChecksumAddress) -> NuNits: + def get_balance(self, address: ChecksumAddress) -> types.NuNits: """Get the NU balance (in NuNits) of a token holder address, or of this contract address""" balance: int = self.contract.functions.balanceOf(address).call() - return NuNits(balance) + return types.NuNits(balance) @contract_api(CONTRACT_CALL) - def get_allowance(self, owner: ChecksumAddress, spender: ChecksumAddress) -> NuNits: + def get_allowance( + self, owner: ChecksumAddress, spender: ChecksumAddress + ) -> types.NuNits: """Check the amount of tokens that an owner allowed to a spender""" allowance: int = self.contract.functions.allowance(owner, spender).call() - return NuNits(allowance) + return types.NuNits(allowance) @contract_api(TRANSACTION) - def increase_allowance(self, - transacting_power: TransactingPower, - spender_address: ChecksumAddress, - increase: NuNits - ) -> TxReceipt: + def increase_allowance( + self, + transacting_power: TransactingPower, + spender_address: ChecksumAddress, + increase: types.NuNits, + ) -> TxReceipt: """Increase the allowance of a spender address funded by a sender address""" contract_function: ContractFunction = self.contract.functions.increaseAllowance(spender_address, increase) receipt: TxReceipt = self.blockchain.send_transaction(contract_function=contract_function, @@ -153,11 +156,12 @@ class NucypherTokenAgent(EthereumContractAgent): return receipt @contract_api(TRANSACTION) - def decrease_allowance(self, - transacting_power: TransactingPower, - spender_address: ChecksumAddress, - decrease: NuNits - ) -> TxReceipt: + def decrease_allowance( + self, + transacting_power: TransactingPower, + spender_address: ChecksumAddress, + decrease: types.NuNits, + ) -> TxReceipt: """Decrease the allowance of a spender address funded by a sender address""" contract_function: ContractFunction = self.contract.functions.decreaseAllowance(spender_address, decrease) receipt: TxReceipt = self.blockchain.send_transaction(contract_function=contract_function, @@ -165,11 +169,12 @@ class NucypherTokenAgent(EthereumContractAgent): return receipt @contract_api(TRANSACTION) - def approve_transfer(self, - amount: NuNits, - spender_address: ChecksumAddress, - transacting_power: TransactingPower - ) -> TxReceipt: + def approve_transfer( + self, + amount: types.NuNits, + spender_address: ChecksumAddress, + transacting_power: TransactingPower, + ) -> TxReceipt: """Approve the spender address to transfer an amount of tokens on behalf of the sender address""" self._validate_zero_allowance(amount, spender_address, transacting_power) @@ -181,7 +186,12 @@ class NucypherTokenAgent(EthereumContractAgent): return receipt @contract_api(TRANSACTION) - def transfer(self, amount: NuNits, target_address: ChecksumAddress, transacting_power: TransactingPower) -> TxReceipt: + def transfer( + self, + amount: types.NuNits, + target_address: ChecksumAddress, + transacting_power: TransactingPower, + ) -> TxReceipt: """Transfer an amount of tokens from the sender address to the target address.""" contract_function: ContractFunction = self.contract.functions.transfer(target_address, amount) receipt: TxReceipt = self.blockchain.send_transaction(contract_function=contract_function, @@ -189,13 +199,14 @@ class NucypherTokenAgent(EthereumContractAgent): return receipt @contract_api(TRANSACTION) - def approve_and_call(self, - amount: NuNits, - target_address: ChecksumAddress, - transacting_power: TransactingPower, - call_data: bytes = b'', - gas_limit: Optional[Wei] = None - ) -> TxReceipt: + def approve_and_call( + self, + amount: types.NuNits, + target_address: ChecksumAddress, + transacting_power: TransactingPower, + call_data: bytes = b"", + gas_limit: Optional[Wei] = None, + ) -> TxReceipt: self._validate_zero_allowance(amount, target_address, transacting_power) payload = None @@ -402,10 +413,12 @@ class PREApplicationAgent(EthereumContractAgent): return result @contract_api(CONTRACT_CALL) - def get_staking_provider_info(self, staking_provider: ChecksumAddress) -> StakingProviderInfo: + def get_staking_provider_info( + self, staking_provider: ChecksumAddress + ) -> types.StakingProviderInfo: # remove reserved fields info: list = self.contract.functions.stakingProviderInfo(staking_provider).call() - return StakingProviderInfo(*info[0:3]) + return types.StakingProviderInfo(*info[0:3]) @contract_api(CONTRACT_CALL) def get_authorized_stake(self, staking_provider: ChecksumAddress) -> int: @@ -441,8 +454,9 @@ class PREApplicationAgent(EthereumContractAgent): yield address @contract_api(CONTRACT_CALL) - def get_all_active_staking_providers(self, pagination_size: Optional[int] = None) -> Tuple[TuNits, Dict[ChecksumAddress, TuNits]]: - + def get_all_active_staking_providers( + self, pagination_size: Optional[int] = None + ) -> Tuple[types.TuNits, Dict[ChecksumAddress, types.TuNits]]: if pagination_size is None: pagination_size = self.DEFAULT_PROVIDERS_PAGINATION_SIZE_LIGHT_NODE if self.blockchain.is_light else self.DEFAULT_PROVIDERS_PAGINATION_SIZE self.log.debug(f"Defaulting to pagination size {pagination_size}") @@ -488,10 +502,12 @@ class PREApplicationAgent(EthereumContractAgent): def checksum_address(address: int) -> ChecksumAddress: return ChecksumAddress(to_checksum_address(address.to_bytes(ETH_ADDRESS_BYTE_LENGTH, 'big'))) - typed_staking_providers = {checksum_address(address): TuNits(authorized_tokens) - for address, authorized_tokens in staking_providers.items()} + typed_staking_providers = { + checksum_address(address): types.TuNits(authorized_tokens) + for address, authorized_tokens in staking_providers.items() + } - return TuNits(n_tokens), typed_staking_providers + return types.TuNits(n_tokens), typed_staking_providers def get_staking_provider_reservoir(self, without: Iterable[ChecksumAddress] = None, @@ -760,11 +776,11 @@ class ContractAgency: @classmethod def get_agent( cls, - agent_class: Type[Agent], + agent_class: Type[types.Agent], registry: Optional[BaseContractRegistry], provider_uri: Optional[str], contract_version: Optional[str] = None, - ) -> Agent: + ) -> types.Agent: if not issubclass(agent_class, EthereumContractAgent): raise TypeError("Only agent subclasses can be used from the agency.") @@ -780,10 +796,10 @@ class ContractAgency: registry_id = registry.id try: - return cast(Agent, cls.__agents[registry_id][agent_class]) + return cast(types.Agent, cls.__agents[registry_id][agent_class]) except KeyError: agent = cast( - Agent, + types.Agent, agent_class( registry=registry, provider_uri=provider_uri, diff --git a/nucypher/blockchain/eth/events.py b/nucypher/blockchain/eth/events.py index 0f08e7670..72457384e 100644 --- a/nucypher/blockchain/eth/events.py +++ b/nucypher/blockchain/eth/events.py @@ -3,6 +3,7 @@ import os from web3.contract.contract import Contract +from nucypher.blockchain.eth import agents from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory from nucypher.config.constants import NUCYPHER_EVENTS_THROTTLE_MAX_BLOCKS @@ -71,13 +72,15 @@ class ContractEventsThrottler: # default to 1000 - smallest default heard about so far (alchemy) DEFAULT_MAX_BLOCKS_PER_CALL = int(os.environ.get(NUCYPHER_EVENTS_THROTTLE_MAX_BLOCKS, 1000)) - def __init__(self, - agent: 'EthereumContractAgent', - event_name: str, - from_block: int, - to_block: int = None, # defaults to latest block - max_blocks_per_call: int = DEFAULT_MAX_BLOCKS_PER_CALL, - **argument_filters): + def __init__( + self, + agent: "agents.EthereumContractAgent", + event_name: str, + from_block: int, + to_block: int = None, # defaults to latest block + max_blocks_per_call: int = DEFAULT_MAX_BLOCKS_PER_CALL, + **argument_filters, + ): self.event_filter = agent.events[event_name] self.from_block = from_block self.to_block = to_block if to_block is not None else agent.blockchain.client.block_number diff --git a/nucypher/blockchain/eth/trackers/dkg.py b/nucypher/blockchain/eth/trackers/dkg.py index b61fe4c76..d2bdb281f 100644 --- a/nucypher/blockchain/eth/trackers/dkg.py +++ b/nucypher/blockchain/eth/trackers/dkg.py @@ -6,6 +6,7 @@ from twisted.internet import threads from web3.contract.contract import ContractEvent from web3.datastructures import AttributeDict +from nucypher.blockchain.eth.actors import Ritualist from nucypher.policy.conditions.utils import camel_case_to_snake from nucypher.utilities.events import EventScanner, JSONifiedState from nucypher.utilities.logging import Logger @@ -66,7 +67,7 @@ class ActiveRitualTracker: def __init__( self, - ritualist: "Ritualist", + ritualist: Ritualist, persistent: bool = False, # TODO: use persistent storage? ): self.log = Logger("RitualTracker") diff --git a/nucypher/characters/lawful.py b/nucypher/characters/lawful.py index 43341b3a9..1da8be5fc 100644 --- a/nucypher/characters/lawful.py +++ b/nucypher/characters/lawful.py @@ -68,7 +68,7 @@ from web3.types import TxReceipt import nucypher from nucypher.acumen.nicknames import Nickname from nucypher.acumen.perception import ArchivedFleetState, RemoteUrsulaStatus -from nucypher.blockchain.eth.actors import Operator, PolicyAuthor, Ritualist +from nucypher.blockchain.eth import actors from nucypher.blockchain.eth.agents import ( ContractAgency, CoordinatorAgent, @@ -100,6 +100,7 @@ from nucypher.crypto.powers import ( TLSHostingPower, TransactingPower, ) +from nucypher.network import trackers from nucypher.network.decryption import ThresholdDecryptionClient from nucypher.network.exceptions import NodeSeemsToBeDown from nucypher.network.middleware import RestMiddleware @@ -107,7 +108,6 @@ from nucypher.network.nodes import TEACHER_NODES, NodeSprout, Teacher from nucypher.network.protocols import parse_node_uri from nucypher.network.retrieval import PRERetrievalClient from nucypher.network.server import ProxyRESTServer, make_rest_app -from nucypher.network.trackers import AvailabilityTracker from nucypher.policy.conditions.types import Lingo from nucypher.policy.conditions.utils import validate_condition_lingo from nucypher.policy.kits import PolicyMessageKit @@ -116,9 +116,10 @@ from nucypher.policy.policies import Policy from nucypher.utilities.emitters import StdoutEmitter from nucypher.utilities.logging import Logger from nucypher.utilities.networking import validate_operator_ip +from nucypher.utilities.prometheus.metrics import PrometheusMetricsConfig -class Alice(Character, PolicyAuthor): +class Alice(Character, actors.PolicyAuthor): banner = ALICE_BANNER _default_crypto_powerups = [SigningPower, DecryptingPower, DelegatingPower] @@ -183,7 +184,7 @@ class Alice(Character, PolicyAuthor): account=checksum_address, signer=signer ) self._crypto_power.consume_power_up(self.transacting_power) - PolicyAuthor.__init__( + actors.PolicyAuthor.__init__( self, domain=self.domain, transacting_power=self.transacting_power, @@ -369,7 +370,7 @@ class Alice(Character, PolicyAuthor): def revoke( self, policy: Policy, onchain: bool = True, offchain: bool = True - ) -> Tuple[TxReceipt, Dict[ChecksumAddress, Tuple["Revocation", Exception]]]: + ) -> Tuple[TxReceipt, Dict[ChecksumAddress, Tuple["actors.Revocation", Exception]]]: if not (offchain or onchain): raise ValueError("offchain or onchain must be True to issue revocation") @@ -798,7 +799,7 @@ class Bob(Character): return cleartext -class Ursula(Teacher, Character, Operator, Ritualist): +class Ursula(Teacher, Character, actors.Operator, actors.Ritualist): banner = URSULA_BANNER _alice_class = Alice @@ -864,11 +865,11 @@ class Ursula(Teacher, Character, Operator, Ritualist): # Health Checks self._availability_check = availability_check - self._availability_tracker = AvailabilityTracker(ursula=self) + self._availability_tracker = trackers.AvailabilityTracker(ursula=self) try: payment_method: ContractPayment - Operator.__init__( + actors.Operator.__init__( self, is_me=is_me, domain=self.domain, @@ -882,7 +883,7 @@ class Ursula(Teacher, Character, Operator, Ritualist): ) # DKG Ritualist - Ritualist.__init__( + actors.Ritualist.__init__( self, domain=domain, provider_uri=payment_method.provider, @@ -1010,7 +1011,7 @@ class Ursula(Teacher, Character, Operator, Ritualist): ritualist: bool = True, hendrix: bool = True, start_reactor: bool = True, - prometheus_config: "PrometheusMetricsConfig" = None, + prometheus_config: PrometheusMetricsConfig = None, preflight: bool = True, block_until_ready: bool = True, eager: bool = False, diff --git a/nucypher/network/middleware.py b/nucypher/network/middleware.py index bf2a7ada2..877e1304c 100644 --- a/nucypher/network/middleware.py +++ b/nucypher/network/middleware.py @@ -12,8 +12,9 @@ from cryptography.hazmat.backends import default_backend from nucypher_core import FleetStateChecksum, MetadataRequest, NodeMetadata from requests.exceptions import SSLError +from nucypher import characters from nucypher.blockchain.eth.registry import BaseContractRegistry -from nucypher.config.storages import ForgetfulNodeStorage +from nucypher.config.storages import ForgetfulNodeStorage, NodeStorage from nucypher.network.exceptions import NodeSeemsToBeDown from nucypher.utilities.logging import Logger @@ -29,7 +30,7 @@ class NucypherMiddlewareClient: self, eth_provider_uri: Optional[str], registry: Optional["BaseContractRegistry"] = None, - storage: Optional["NodeStorage"] = None, + storage: Optional[NodeStorage] = None, *args, **kwargs, ): @@ -260,7 +261,12 @@ class RestMiddleware: ) return response - def reencrypt(self, ursula: "Ursula", reencryption_request_bytes: bytes, timeout=8): + def reencrypt( + self, + ursula: "characters.lawful.Ursula", + reencryption_request_bytes: bytes, + timeout=8, + ): response = self.client.post( node_or_sprout=ursula, path="reencrypt", @@ -270,7 +276,10 @@ class RestMiddleware: return response def get_encrypted_decryption_share( - self, ursula: "Ursula", decryption_request_bytes: bytes, timeout=8 + self, + ursula: "characters.lawful.Ursula", + decryption_request_bytes: bytes, + timeout=8, ): response = self.client.post( node_or_sprout=ursula, diff --git a/nucypher/network/nodes.py b/nucypher/network/nodes.py index 112be9d05..45be30c73 100644 --- a/nucypher/network/nodes.py +++ b/nucypher/network/nodes.py @@ -23,6 +23,7 @@ from requests.exceptions import SSLError from twisted.internet import reactor, task from twisted.internet.defer import Deferred +from nucypher import characters from nucypher.acumen.nicknames import Nickname from nucypher.acumen.perception import FleetSensor from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent @@ -150,7 +151,6 @@ class NodeSprout: return self._metadata_payload.domain def finish(self): - from nucypher.characters.lawful import Ursula # Remote node cryptographic material crypto_power = CryptoPower() @@ -167,16 +167,18 @@ class NodeSprout: self._metadata_payload.certificate_der, backend=default_backend() ) - return Ursula(is_me=False, - crypto_power=crypto_power, - rest_host=self._metadata_payload.host, - rest_port=self._metadata_payload.port, - checksum_address=self.checksum_address, - domain=self._metadata_payload.domain, - timestamp=self.timestamp, - operator_signature_from_metadata=self.operator_signature_from_metadata, - certificate=tls_certificate, - metadata=self._metadata) + return characters.lawful.Ursula( + is_me=False, + crypto_power=crypto_power, + rest_host=self._metadata_payload.host, + rest_port=self._metadata_payload.port, + checksum_address=self.checksum_address, + domain=self._metadata_payload.domain, + timestamp=self.timestamp, + operator_signature_from_metadata=self.operator_signature_from_metadata, + certificate=tls_certificate, + metadata=self._metadata, + ) def mature(self): if self._is_finishing: @@ -293,8 +295,7 @@ class Learner: if save_metadata and node_storage is NO_STORAGE_AVAILABLE: raise ValueError("Cannot save nodes without a configured node storage") - from nucypher.characters.lawful import Ursula - self.node_class = node_class or Ursula + self.node_class = node_class or characters.lawful.Ursula self.node_class.set_cert_storage_function( node_storage.store_node_certificate) # TODO: Fix this temporary workaround for on-disk cert storage. #1481 @@ -745,13 +746,15 @@ class Learner: def write_node_metadata(self, node, serializer=bytes) -> str: return self.node_storage.store_node_metadata(node=node) - def verify_from(self, - stranger: 'Character', - message: bytes, - signature: Signature - ): - - if not signature.verify(verifying_pk=stranger.stamp.as_umbral_pubkey(), message=message): + def verify_from( + self, + stranger: "characters.base.Character", + message: bytes, + signature: Signature, + ): + if not signature.verify( + verifying_pk=stranger.stamp.as_umbral_pubkey(), message=message + ): try: node_on_the_other_end = self.node_class.from_seednode_metadata( stranger.seed_node_metadata(), diff --git a/nucypher/network/retrieval.py b/nucypher/network/retrieval.py index fe1639bfe..aaffe1594 100644 --- a/nucypher/network/retrieval.py +++ b/nucypher/network/retrieval.py @@ -22,6 +22,7 @@ from nucypher_core.umbral import ( VerifiedCapsuleFrag, ) +from nucypher.characters import lawful from nucypher.crypto.signing import InvalidSignature from nucypher.network.client import ThresholdAccessControlClient from nucypher.network.exceptions import NodeSeemsToBeDown @@ -182,13 +183,14 @@ class PRERetrievalClient(ThresholdAccessControlClient): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - def _request_reencryption(self, - ursula: 'Ursula', - reencryption_request: ReencryptionRequest, - alice_verifying_key: PublicKey, - policy_encrypting_key: PublicKey, - bob_encrypting_key: PublicKey, - ) -> Dict['Capsule', 'VerifiedCapsuleFrag']: + def _request_reencryption( + self, + ursula: "lawful.Ursula", + reencryption_request: ReencryptionRequest, + alice_verifying_key: PublicKey, + policy_encrypting_key: PublicKey, + bob_encrypting_key: PublicKey, + ) -> Dict["Capsule", "VerifiedCapsuleFrag"]: """ Sends a reencryption request to a single Ursula and processes the results. diff --git a/nucypher/network/trackers.py b/nucypher/network/trackers.py index da0f4b608..0e492d56a 100644 --- a/nucypher/network/trackers.py +++ b/nucypher/network/trackers.py @@ -8,12 +8,13 @@ from twisted.internet import reactor from twisted.internet.task import LoopingCall from twisted.python.failure import Failure +from nucypher import characters from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent from nucypher.blockchain.eth.constants import NULL_ADDRESS -from nucypher.utilities.emitters import StdoutEmitter from nucypher.network.exceptions import NodeSeemsToBeDown from nucypher.network.middleware import RestMiddleware from nucypher.network.nodes import NodeSprout +from nucypher.utilities.emitters import StdoutEmitter from nucypher.utilities.logging import Logger from nucypher.utilities.task import SimpleTask @@ -263,7 +264,9 @@ class AvailabilityTracker: self.log.debug(f'{ursula_or_sprout} responded to uptime check with {e.__class__.__name__}') continue - def measure(self, ursula_or_sprout: Union['Ursula', NodeSprout]) -> None: + def measure( + self, ursula_or_sprout: Union["characters.lawful.Ursula", NodeSprout] + ) -> None: """Measure self-availability from a single remote node that participates uptime checks.""" try: response = self._ursula.network_middleware.check_availability(initiator=self._ursula, responder=ursula_or_sprout) diff --git a/nucypher/policy/payment.py b/nucypher/policy/payment.py index c63f2b57d..44f736718 100644 --- a/nucypher/policy/payment.py +++ b/nucypher/policy/payment.py @@ -10,7 +10,7 @@ from nucypher.blockchain.eth.registry import ( BaseContractRegistry, InMemoryContractRegistry, ) -from nucypher.policy.policies import Policy +from nucypher.policy import policies class PaymentMethod(ABC): @@ -25,7 +25,7 @@ class PaymentMethod(ABC): shares: int @abstractmethod - def pay(self, policy: Policy) -> Dict: + def pay(self, policy: "policies.Policy") -> Dict: """Carry out payment for the given policy.""" raise NotImplementedError @@ -101,7 +101,7 @@ class SubscriptionManagerPayment(ContractPayment): result = self.agent.is_policy_active(policy_id=bytes(request.hrac)) return result - def pay(self, policy: Policy) -> TxReceipt: + def pay(self, policy: "policies.Policy") -> TxReceipt: """Writes a new policy to the SubscriptionManager contract.""" receipt = self.agent.create_policy( value=policy.value, # wei @@ -176,7 +176,7 @@ class FreeReencryptions(PaymentMethod): def verify(self, payee: ChecksumAddress, request: ReencryptionRequest) -> bool: return True - def pay(self, policy: Policy) -> Dict: + def pay(self, policy: "policies.Policy") -> Dict: receipt = f'Receipt for free policy {bytes(policy.hrac).hex()}.' return dict(receipt=receipt.encode()) diff --git a/nucypher/policy/policies.py b/nucypher/policy/policies.py index 5ab4b758e..f35d8b9d8 100644 --- a/nucypher/policy/policies.py +++ b/nucypher/policy/policies.py @@ -2,11 +2,13 @@ from typing import Dict, Iterable, List, Optional, Sequence import maya from eth_typing.evm import ChecksumAddress -from nucypher_core import HRAC, Address, TreasureMap +from nucypher_core import HRAC, Address, EncryptedTreasureMap, TreasureMap from nucypher_core.umbral import PublicKey, VerifiedKeyFrag +from nucypher import characters from nucypher.crypto.powers import DecryptingPower from nucypher.network.middleware import RestMiddleware +from nucypher.policy import payment from nucypher.policy.reservoir import PrefetchStrategy, make_staking_provider_reservoir from nucypher.policy.revocation import RevocationKit from nucypher.utilities.concurrency import WorkerPool @@ -29,21 +31,21 @@ class Policy: number of available qualified network nodes. """ - def __init__(self, - publisher: 'Alice', - label: bytes, - bob: 'Bob', - kfrags: Sequence[VerifiedKeyFrag], - public_key: PublicKey, - threshold: int, - expiration: maya.MayaDT, - commencement: maya.MayaDT, - value: int, - rate: int, - duration: int, - payment_method: 'PaymentMethod' - ): - + def __init__( + self, + publisher: "characters.lawful.Alice", + label: bytes, + bob: "characters.lawful.Bob", + kfrags: Sequence[VerifiedKeyFrag], + public_key: PublicKey, + threshold: int, + expiration: maya.MayaDT, + commencement: maya.MayaDT, + value: int, + rate: int, + duration: int, + payment_method: "payment.PaymentMethod", + ): self.threshold = threshold self.shares = len(kfrags) self.label = label @@ -75,12 +77,14 @@ class Policy: ) return reservoir - def _publish(self, ursulas: List['Ursula']) -> Dict: + def _publish(self, ursulas: List["characters.lawful.Ursula"]) -> Dict: self.nodes = [ursula.checksum_address for ursula in ursulas] receipt = self.payment_method.pay(policy=self) return receipt - def _ping_node(self, address: ChecksumAddress, network_middleware: RestMiddleware) -> 'Ursula': + def _ping_node( + self, address: ChecksumAddress, network_middleware: RestMiddleware + ) -> "characters.lawful.Ursula": # Handles edge case when provided address is not a known peer. if address not in self.publisher.known_nodes: raise RuntimeError(f"{address} is not a known peer") @@ -94,11 +98,12 @@ class Policy: else: raise RuntimeError(f"{ursula} is not available for selection ({status_code}).") - def _sample(self, - network_middleware: RestMiddleware, - ursulas: Optional[Iterable['Ursula']] = None, - timeout: int = 10, - ) -> List['Ursula']: + def _sample( + self, + network_middleware: RestMiddleware, + ursulas: Optional[Iterable["characters.lawful.Ursula"]] = None, + timeout: int = 10, + ) -> List["characters.lawful.Ursula"]: """Send concurrent requests to the /ping HTTP endpoint of nodes drawn from the reservoir.""" ursulas = ursulas or [] @@ -108,7 +113,7 @@ class Policy: reservoir = self._make_reservoir(handpicked_addresses) value_factory = PrefetchStrategy(reservoir, self.shares) - def worker(address) -> 'Ursula': + def worker(address) -> "characters.lawful.Ursula": return self._ping_node(address, network_middleware) worker_pool = WorkerPool( @@ -142,7 +147,11 @@ class Policy: ursulas = list(successes.values()) return ursulas - def enact(self, network_middleware: RestMiddleware, ursulas: Optional[Iterable['Ursula']] = None) -> 'EnactedPolicy': + def enact( + self, + network_middleware: RestMiddleware, + ursulas: Optional[Iterable["characters.lawful.Ursula"]] = None, + ) -> "EnactedPolicy": """Attempts to enact the policy, returns an `EnactedPolicy` object on success.""" ursulas = self._sample(network_middleware=network_middleware, ursulas=ursulas) @@ -177,16 +186,16 @@ class Policy: class EnactedPolicy: - - def __init__(self, - hrac: HRAC, - label: bytes, - public_key: PublicKey, - threshold: int, - treasure_map: 'EncryptedTreasureMap', - revocation_kit: RevocationKit, - publisher_verifying_key: PublicKey): - + def __init__( + self, + hrac: HRAC, + label: bytes, + public_key: PublicKey, + threshold: int, + treasure_map: EncryptedTreasureMap, + revocation_kit: RevocationKit, + publisher_verifying_key: PublicKey, + ): self.hrac = hrac self.label = label self.public_key = public_key diff --git a/nucypher/types.py b/nucypher/types.py index 0194c687f..9573c58cc 100644 --- a/nucypher/types.py +++ b/nucypher/types.py @@ -3,12 +3,16 @@ from typing import NamedTuple, NewType, TypeVar, Union from eth_typing.evm import ChecksumAddress from web3.types import TxReceipt, Wei +from nucypher.blockchain.eth import agents + ERC20UNits = NewType("ERC20UNits", int) NuNits = NewType("NuNits", ERC20UNits) TuNits = NewType("TuNits", ERC20UNits) -Agent = TypeVar('Agent', bound='EthereumContractAgent') -ContractReturnValue = TypeVar('ContractReturnValue', bound=Union[TxReceipt, Wei, int, str, bool]) +Agent = TypeVar("Agent", bound="agents.EthereumContractAgent") +ContractReturnValue = TypeVar( + "ContractReturnValue", bound=Union[TxReceipt, Wei, int, str, bool] +) class StakingProviderInfo(NamedTuple): diff --git a/nucypher/utilities/prometheus/collector.py b/nucypher/utilities/prometheus/collector.py index 84785fc50..d3e113b47 100644 --- a/nucypher/utilities/prometheus/collector.py +++ b/nucypher/utilities/prometheus/collector.py @@ -15,7 +15,7 @@ from typing import Dict, Type from eth_typing.evm import ChecksumAddress import nucypher -from nucypher.blockchain.eth.actors import NucypherTokenActor +from nucypher.blockchain.eth import actors from nucypher.blockchain.eth.agents import ( ContractAgency, EthereumContractAgent, @@ -23,6 +23,7 @@ from nucypher.blockchain.eth.agents import ( ) from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory from nucypher.blockchain.eth.registry import BaseContractRegistry +from nucypher.characters import lawful class MetricsCollector(ABC): @@ -73,7 +74,8 @@ class BaseMetricsCollector(MetricsCollector): class UrsulaInfoMetricsCollector(BaseMetricsCollector): """Collector for Ursula specific metrics.""" - def __init__(self, ursula: 'Ursula'): + + def __init__(self, ursula: "lawful.Ursula"): super().__init__() self.ursula = ursula @@ -210,7 +212,7 @@ class OperatorMetricsCollector(BaseMetricsCollector): } def _collect_internal(self) -> None: - operator_token_actor = NucypherTokenActor( + operator_token_actor = actors.NucypherTokenActor( registry=self.contract_registry, domain=self.domain, checksum_address=self.operator_address, diff --git a/nucypher/utilities/prometheus/metrics.py b/nucypher/utilities/prometheus/metrics.py index 2ecb3308a..7d8661422 100644 --- a/nucypher/utilities/prometheus/metrics.py +++ b/nucypher/utilities/prometheus/metrics.py @@ -14,6 +14,7 @@ from typing import List from twisted.internet import reactor, task from twisted.web.resource import Resource +from nucypher.characters import lawful from nucypher.utilities.prometheus.collector import ( BlockchainMetricsCollector, MetricsCollector, @@ -118,9 +119,11 @@ def collect_prometheus_metrics(metrics_collectors: List[MetricsCollector]) -> No collector.collect() -def start_prometheus_exporter(ursula: 'Ursula', - prometheus_config: PrometheusMetricsConfig, - registry: CollectorRegistry = REGISTRY) -> None: +def start_prometheus_exporter( + ursula: "lawful.Ursula", + prometheus_config: PrometheusMetricsConfig, + registry: CollectorRegistry = REGISTRY, +) -> None: """Configure, collect, and serve prometheus metrics.""" from prometheus_client.twisted import MetricsResource from twisted.web.resource import Resource @@ -148,7 +151,7 @@ def start_prometheus_exporter(ursula: 'Ursula', reactor.listenTCP(prometheus_config.port, factory, interface=prometheus_config.listen_address) -def create_metrics_collectors(ursula: "Ursula") -> List[MetricsCollector]: +def create_metrics_collectors(ursula: "lawful.Ursula") -> List[MetricsCollector]: """Create collectors used to obtain metrics.""" collectors: List[MetricsCollector] = [UrsulaInfoMetricsCollector(ursula=ursula)] diff --git a/tests/mock/agents.py b/tests/mock/agents.py index 994b5b131..3d343ed13 100644 --- a/tests/mock/agents.py +++ b/tests/mock/agents.py @@ -5,9 +5,10 @@ from unittest.mock import Mock from constant_sorrow.constants import CONTRACT_ATTRIBUTE, CONTRACT_CALL, TRANSACTION from nucypher.blockchain.eth import agents -from nucypher.blockchain.eth.agents import Agent, ContractAgency, EthereumContractAgent +from nucypher.blockchain.eth.agents import ContractAgency, EthereumContractAgent from nucypher.blockchain.eth.constants import NULL_ADDRESS from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory +from nucypher.types import Agent from tests.constants import MOCK_ETH_PROVIDER_URI from tests.mock.interfaces import MockBlockchain