Esempio n. 1
0
    def __init__(self,
                 domain: str = None,
                 registry: BaseContractRegistry = None,
                 controller: bool = True,
                 federated_only: bool = False,
                 node_class: object = Ursula,
                 eth_provider_uri: str = None,
                 execution_timeout: int = DEFAULT_EXECUTION_TIMEOUT,
                 *args,
                 **kwargs):
        self.federated_only = federated_only

        if not self.federated_only:
            if not eth_provider_uri:
                raise ValueError(
                    'ETH Provider URI is required for decentralized Porter.')

            if not BlockchainInterfaceFactory.is_interface_initialized(
                    eth_provider_uri=eth_provider_uri):
                BlockchainInterfaceFactory.initialize_interface(
                    eth_provider_uri=eth_provider_uri)

            self.registry = registry or InMemoryContractRegistry.from_latest_publication(
                network=domain)
            self.application_agent = ContractAgency.get_agent(
                PREApplicationAgent, registry=self.registry)
        else:
            self.registry = NO_BLOCKCHAIN_CONNECTION.bool_value(False)
            node_class.set_federated_mode(federated_only)

        super().__init__(save_metadata=True,
                         domain=domain,
                         node_class=node_class,
                         *args,
                         **kwargs)

        self.log = Logger(self.__class__.__name__)
        self.execution_timeout = execution_timeout

        # Controller Interface
        self.interface = self._interface_class(porter=self)
        self.controller = NO_CONTROL_PROTOCOL
        if controller:
            # TODO need to understand this better - only made it analogous to what was done for characters
            self.make_cli_controller()
        self.log.info(self.BANNER)
Esempio n. 2
0
    def __init__(self,
                 domains: Set = None,
                 known_node_class: object = None,
                 is_me: bool = True,
                 federated_only: bool = False,
                 checksum_address: str = NO_BLOCKCHAIN_CONNECTION.bool_value(
                     False),
                 network_middleware: RestMiddleware = None,
                 keyring: NucypherKeyring = None,
                 keyring_root: str = None,
                 crypto_power: CryptoPower = None,
                 crypto_power_ups: List[CryptoPowerUp] = None,
                 provider_uri: str = None,
                 signer: Signer = None,
                 registry: BaseContractRegistry = None,
                 *args,
                 **kwargs) -> None:
        """

        Base class for Nucypher protocol actors.


        PowerUps
        ========
        :param crypto_power: A CryptoPower object; if provided, this will be the character's CryptoPower.
        :param crypto_power_ups: If crypto_power is not provided, a new one will be made to consume all CryptoPowerUps.

        If neither crypto_power nor crypto_power_ups are provided, we give this
        Character all CryptoPowerUps listed in their _default_crypto_powerups
        attribute.

        :param is_me: Set this to True when you want this Character to represent
            the owner of the configuration under which the program is being run.
            A Character who is_me can do things that other Characters can't,
            like run servers, sign messages, and decrypt messages which are
            encrypted for them.  Typically this will be True for exactly one
            Character, but there are scenarios in which its imaginable to be
            represented by zero Characters or by more than one Character.

        """

        #
        # Operating Mode
        if hasattr(self, '_interface_class'
                   ):  # TODO: have argument about meaning of 'lawful'
            #                                         and whether maybe only Lawful characters have an interface
            self.interface = self._interface_class(character=self)
        if is_me:
            if not known_node_class:
                # Once in a while, in tests or demos, we init a plain Character who doesn't already know about its node class.
                from nucypher.characters.lawful import Ursula
                known_node_class = Ursula
            # If we're federated only, we assume that all other nodes in our domain are as well.
            known_node_class.set_federated_mode(federated_only)
        else:
            # What an awful hack.  The last convulsions of #466.
            # TODO: Anything else.
            with suppress(AttributeError):
                federated_only = known_node_class._federated_only_instances

        if federated_only:
            if registry or provider_uri:
                raise ValueError(
                    f"Cannot init federated-only character with {registry or provider_uri}."
                )
        self.federated_only = bool(federated_only)  # type: bool

        #
        # Powers
        #

        # Derive powers from keyring
        if keyring_root and keyring:
            if keyring_root != keyring.keyring_root:
                raise ValueError("Inconsistent keyring root directory path")
        if keyring:
            keyring_root, checksum_address = keyring.keyring_root, keyring.checksum_address
            crypto_power_ups = list()
            for power_up in self._default_crypto_powerups:
                power = keyring.derive_crypto_power(power_class=power_up)
                crypto_power_ups.append(power)
        self.keyring_root = keyring_root
        self.keyring = keyring

        if crypto_power and crypto_power_ups:
            raise ValueError(
                "Pass crypto_power or crypto_power_ups (or neither), but not both."
            )
        crypto_power_ups = crypto_power_ups or list()  # type: list

        if crypto_power:
            self._crypto_power = crypto_power  # type: CryptoPower
        elif crypto_power_ups:
            self._crypto_power = CryptoPower(power_ups=crypto_power_ups)
        else:
            self._crypto_power = CryptoPower(
                power_ups=self._default_crypto_powerups)

        self._checksum_address = checksum_address

        # Fleet and Blockchain Connection (Everyone)
        if not domains:
            domains = {CharacterConfiguration.DEFAULT_DOMAIN}

        #
        # Self-Character
        #

        if is_me:
            self.treasure_maps = {}  # type: dict

            #
            # Signing Power
            #
            self.signer = signer
            try:
                signing_power = self._crypto_power.power_ups(
                    SigningPower)  # type: SigningPower
                self._stamp = signing_power.get_signature_stamp(
                )  # type: SignatureStamp
            except NoSigningPower:
                self._stamp = NO_SIGNING_POWER

            #
            # Blockchain
            #
            self.provider_uri = provider_uri
            if not self.federated_only:
                self.registry = registry or InMemoryContractRegistry.from_latest_publication(
                    network=list(domains)[0])  #TODO: #1580
            else:
                self.registry = NO_BLOCKCHAIN_CONNECTION.bool_value(False)

            # REST
            self.network_middleware = network_middleware or RestMiddleware(
                registry=self.registry)

            #
            # Learner
            #
            Learner.__init__(self,
                             domains=domains,
                             network_middleware=self.network_middleware,
                             node_class=known_node_class,
                             *args,
                             **kwargs)

        #
        # Stranger-Character
        #

        else:  # Feel like a stranger
            if network_middleware is not None:
                raise TypeError(
                    "Network middleware cannot be attached to a Stranger-Character."
                )

            if registry is not None:
                raise TypeError(
                    "Registry cannot be attached to stranger-Characters.")

            verifying_key = self.public_keys(SigningPower)
            self._stamp = StrangerStamp(verifying_key)
            self.keyring_root = STRANGER
            self.network_middleware = STRANGER

        #
        # Decentralized
        #
        if not federated_only:
            self._checksum_address = checksum_address  # TODO: Check that this matches TransactingPower

        #
        # Federated
        #
        elif federated_only:
            try:
                self._set_checksum_address()  # type: str
            except NoSigningPower:
                self._checksum_address = NO_BLOCKCHAIN_CONNECTION
            if checksum_address:
                # We'll take a checksum address, as long as it matches their singing key
                if not checksum_address == self.checksum_address:
                    error = "Federated-only Characters derive their address from their Signing key; got {} instead."
                    raise self.SuspiciousActivity(
                        error.format(checksum_address))

        #
        # Nicknames
        #
        if self._checksum_address is NO_BLOCKCHAIN_CONNECTION and not self.federated_only and not is_me:
            # Sometimes we don't care about the nickname.  For example, if Alice is granting to Bob, she usually
            # doesn't know or care about his wallet.  Maybe this needs to change?
            # Currently, if this is a stranger and there's no blockchain connection, we assign NO_NICKNAME:
            self.nickname = self.nickname_metadata = NO_NICKNAME
        else:
            try:
                self.nickname, self.nickname_metadata = nickname_from_seed(
                    self.checksum_address)
            except SigningPower.not_found_error:  # TODO: Handle NO_BLOCKCHAIN_CONNECTION more coherently - #1547
                if self.federated_only:
                    self.nickname = self.nickname_metadata = NO_NICKNAME
                else:
                    raise

        #
        # Fleet state
        #
        if is_me is True:
            self.known_nodes.record_fleet_state()

        #
        # Character Control
        #
        self.controller = NO_CONTROL_PROTOCOL
Esempio n. 3
0
import requests
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION, NO_PASSWORD, NO_CONTROL_PROTOCOL
from nacl.exceptions import CryptoError
from twisted.logger import Logger

from nucypher.blockchain.eth.clients import NuCypherGethGoerliProcess
from nucypher.blockchain.eth.decorators import validate_checksum_address
from nucypher.blockchain.eth.token import Stake
from nucypher.characters.lawful import Ursula
from nucypher.cli import painting
from nucypher.cli.types import IPV4_ADDRESS
from nucypher.config.node import CharacterConfiguration
from nucypher.network.middleware import RestMiddleware
from nucypher.network.teachers import TEACHER_NODES

NO_BLOCKCHAIN_CONNECTION.bool_value(False)

DESTRUCTION = '''
*Permanently and irreversibly delete all* nucypher files including:
    - Private and Public Keys
    - Known Nodes
    - TLS certificates
    - Node Configurations

Delete {}?'''

CHARACTER_DESTRUCTION = '''
Delete all {name} character files including:
    - Private and Public Keys ({keystore})
    - Known Nodes             ({nodestore})
    - Node Configuration File ({config})
Esempio n. 4
0
    def __init__(self,
                 domains: Set = (GLOBAL_DOMAIN,),
                 is_me: bool = True,
                 federated_only: bool = False,
                 blockchain: Blockchain = None,
                 checksum_public_address: str = NO_BLOCKCHAIN_CONNECTION.bool_value(False),
                 network_middleware: RestMiddleware = None,
                 keyring_dir: str = None,
                 crypto_power: CryptoPower = None,
                 crypto_power_ups: List[CryptoPowerUp] = None,
                 *args, **kwargs
                 ) -> None:

        """

        Base class for Nucypher protocol actors.


        PowerUps
        ========
        :param crypto_power: A CryptoPower object; if provided, this will be the character's CryptoPower.
        :param crypto_power_ups: If crypto_power is not provided, a new one will be made to consume all CryptoPowerUps.

        If neither crypto_power nor crypto_power_ups are provided, we give this
        Character all CryptoPowerUps listed in their _default_crypto_powerups
        attribute.

        :param is_me: Set this to True when you want this Character to represent
            the owner of the configuration under which the program is being run.
            A Character who is_me can do things that other Characters can't,
            like run servers, sign messages, and decrypt messages which are
            encrypted for them.  Typically this will be True for exactly one
            Character, but there are scenarios in which its imaginable to be
            represented by zero Characters or by more than one Character.

        """

        self.federated_only = federated_only  # type: bool

        #
        # Powers
        #
        if crypto_power and crypto_power_ups:
            raise ValueError("Pass crypto_power or crypto_power_ups (or neither), but not both.")
        crypto_power_ups = crypto_power_ups or list()  # type: list

        if crypto_power:
            self._crypto_power = crypto_power  # type: CryptoPower
        elif crypto_power_ups:
            self._crypto_power = CryptoPower(power_ups=crypto_power_ups)
        else:
            self._crypto_power = CryptoPower(power_ups=self._default_crypto_powerups)

        self._checksum_address = checksum_public_address
        #
        # Self-Character
        #
        if is_me is True:
            if not self.federated_only:
                self.blockchain = blockchain or Blockchain.connect()

            self.keyring_dir = keyring_dir  # type: str
            self.treasure_maps = {}  # type: dict
            self.network_middleware = network_middleware or RestMiddleware()

            #
            # Signing Power
            #
            try:
                signing_power = self._crypto_power.power_ups(SigningPower)  # type: SigningPower
                self._stamp = signing_power.get_signature_stamp()  # type: SignatureStamp
            except NoSigningPower:
                self._stamp = NO_SIGNING_POWER

            #
            # Learner
            #
            Learner.__init__(self,
                             domains=domains,
                             network_middleware=network_middleware,
                             *args, **kwargs)

        #
        # Stranger-Character
        #
        else:  # Feel like a stranger
            if network_middleware is not None:
                raise TypeError("Network middleware cannot be attached to a Stranger-Character.")
            self._stamp = StrangerStamp(self.public_keys(SigningPower))
            self.keyring_dir = STRANGER
            self.network_middleware = STRANGER

        #
        # Decentralized
        #
        if not federated_only:
            if not checksum_public_address:
                raise ValueError("No checksum_public_address provided while running in a non-federated mode.")
            else:
                self._checksum_address = checksum_public_address  # TODO: Check that this matches BlockchainPower
        #
        # Federated
        #
        elif federated_only:
            try:
                self._set_checksum_address()  # type: str
            except NoSigningPower:
                self._checksum_address = NO_BLOCKCHAIN_CONNECTION
            if checksum_public_address:
                # We'll take a checksum address, as long as it matches their singing key
                if not checksum_public_address == self.checksum_public_address:
                    error = "Federated-only Characters derive their address from their Signing key; got {} instead."
                    raise self.SuspiciousActivity(error.format(checksum_public_address))

        #
        # Nicknames
        #
        try:
            self.nickname, self.nickname_metadata = nickname_from_seed(self.checksum_public_address)
        except SigningPower.not_found_error:
            if self.federated_only:
                self.nickname = self.nickname_metadata = NO_NICKNAME
            else:
                raise

        #
        # Fleet state
        #
        if is_me is True:
            self.known_nodes.record_fleet_state()

        #
        # Character Control
        #
        self.controller = NO_CONTROL_PROTOCOL
Esempio n. 5
0
    def __init__(self,

                 # Base
                 config_root: str = None,
                 filepath: str = None,

                 # Mode
                 dev_mode: bool = False,
                 federated_only: bool = False,

                 # Identity
                 checksum_address: str = None,
                 crypto_power: CryptoPower = None,

                 # Keyring
                 keyring: NucypherKeyring = None,
                 keyring_root: str = None,

                 # Learner
                 learn_on_same_thread: bool = False,
                 abort_on_learning_error: bool = False,
                 start_learning_now: bool = True,

                 # Network
                 controller_port: int = None,
                 domains: Set[str] = None,  # TODO: Mapping between learning domains and "registry" domains - #1580
                 interface_signature: Signature = None,
                 network_middleware: RestMiddleware = None,

                 # Node Storage
                 known_nodes: set = None,
                 node_storage: NodeStorage = None,
                 reload_metadata: bool = True,
                 save_metadata: bool = True,

                 # Blockchain
                 poa: bool = False,
                 light: bool = False,
                 sync: bool = False,
                 provider_uri: str = None,
                 provider_process=None,
                 gas_strategy: Union[Callable, str] = DEFAULT_GAS_STRATEGY,

                 # Registry
                 registry: BaseContractRegistry = None,
                 registry_filepath: str = None,

                 emitter=None,
                 ):

        self.log = Logger(self.__class__.__name__)
        UNINITIALIZED_CONFIGURATION.bool_value(False)

        # Identity
        # NOTE: NodeConfigurations can only be used with Self-Characters
        self.is_me = True
        self.checksum_address = checksum_address

        # Network
        self.controller_port = controller_port or self.DEFAULT_CONTROLLER_PORT
        self.network_middleware = network_middleware or self.DEFAULT_NETWORK_MIDDLEWARE()
        self.interface_signature = interface_signature

        # Keyring
        self.crypto_power = crypto_power
        self.keyring = keyring or NO_KEYRING_ATTACHED
        self.keyring_root = keyring_root or UNINITIALIZED_CONFIGURATION

        # Contract Registry
        if registry and registry_filepath:
            if registry.filepath != registry_filepath:
                error = f"Inconsistent registry filepaths for '{registry.filepath}' and '{registry_filepath}'."
                raise ValueError(error)
            else:
                self.log.warn(f"Registry and registry filepath were both passed.")
        self.registry = registry or NO_BLOCKCHAIN_CONNECTION.bool_value(False)
        self.registry_filepath = registry_filepath or UNINITIALIZED_CONFIGURATION

        # Blockchain
        self.poa = poa
        self.is_light = light
        self.provider_uri = provider_uri or NO_BLOCKCHAIN_CONNECTION
        self.provider_process = provider_process or NO_BLOCKCHAIN_CONNECTION

        # Learner
        self.federated_only = federated_only
        self.domains = domains or {self.DEFAULT_DOMAIN}
        self.learn_on_same_thread = learn_on_same_thread
        self.abort_on_learning_error = abort_on_learning_error
        self.start_learning_now = start_learning_now
        self.save_metadata = save_metadata
        self.reload_metadata = reload_metadata
        self.known_nodes = known_nodes or set()  # handpicked

        # Configuration
        self.__dev_mode = dev_mode
        self.config_file_location = filepath or UNINITIALIZED_CONFIGURATION
        self.config_root = UNINITIALIZED_CONFIGURATION

        #
        # Federated vs. Blockchain arguments consistency
        #

        #
        # Federated
        #

        if self.federated_only:
            # Check for incompatible values
            blockchain_args = {'filepath': registry_filepath,
                               'poa': poa,
                               'provider_process': provider_process,
                               'provider_uri': provider_uri,
                               'gas_strategy': gas_strategy}
            if any(blockchain_args.values()):
                bad_args = (f"{arg}={val}" for arg, val in blockchain_args.items() if val)
                self.log.warn(f"Arguments {bad_args} are incompatible with federated_only. "
                              f"Overridden with a sane default.")

                # Clear decentralized attributes to ensure consistency with a
                # federated configuration.
                self.poa = False
                self.is_light = False
                self.provider_uri = None
                self.provider_process = None
                self.registry_filepath = None
                self.gas_strategy = None

        #
        # Decentralized
        #

        else:
            self.gas_strategy = gas_strategy
            is_initialized = BlockchainInterfaceFactory.is_interface_initialized(provider_uri=self.provider_uri)
            if not is_initialized and provider_uri:
                BlockchainInterfaceFactory.initialize_interface(provider_uri=self.provider_uri,
                                                                poa=self.poa,
                                                                light=self.is_light,
                                                                provider_process=self.provider_process,
                                                                sync=sync,
                                                                emitter=emitter,
                                                                gas_strategy=gas_strategy)
            else:
                self.log.warn(f"Using existing blockchain interface connection ({self.provider_uri}).")

            if not self.registry:
                # TODO: These two code blocks are untested.
                if not self.registry_filepath:  # TODO: Registry URI  (goerli://speedynet.json) :-)
                    self.log.info(f"Fetching latest registry from source.")
                    self.registry = InMemoryContractRegistry.from_latest_publication(network=list(self.domains)[0])  # TODO: #1580
                else:
                    self.registry = LocalContractRegistry(filepath=self.registry_filepath)
                    self.log.info(f"Using local registry ({self.registry}).")

        if dev_mode:
            self.__temp_dir = UNINITIALIZED_CONFIGURATION
            self.__setup_node_storage()
            self.initialize(password=DEVELOPMENT_CONFIGURATION)
        else:
            self.__temp_dir = LIVE_CONFIGURATION
            self.config_root = config_root or self.DEFAULT_CONFIG_ROOT
            self._cache_runtime_filepaths()
            self.__setup_node_storage(node_storage=node_storage)

        super().__init__(filepath=self.config_file_location, config_root=self.config_root)
Esempio n. 6
0
    def __init__(self,
                 domain: str = None,
                 known_node_class: object = None,
                 is_me: bool = True,
                 federated_only: bool = False,
                 checksum_address: str = None,
                 network_middleware: RestMiddleware = None,
                 keyring: NucypherKeyring = None,
                 crypto_power: CryptoPower = None,
                 crypto_power_ups: List[CryptoPowerUp] = None,
                 provider_uri: str = None,
                 signer: Signer = None,
                 registry: BaseContractRegistry = None,
                 include_self_in_the_state: bool = False,
                 *args,
                 **kwargs) -> None:
        """

        A participant in the cryptological drama (a screenplay, if you like) of NuCypher.

        Characters can represent users, nodes, wallets, offline devices, or other objects of varying levels of abstraction.

        The Named Characters use this class as a Base, and achieve their individuality from additional methods and PowerUps.


        PowerUps
        ========
        :param crypto_power: A CryptoPower object; if provided, this will be the character's CryptoPower.
        :param crypto_power_ups: If crypto_power is not provided, a new one will be made to consume all CryptoPowerUps.

        If neither crypto_power nor crypto_power_ups are provided, we give this
        Character all CryptoPowerUps listed in their _default_crypto_powerups
        attribute.

        :param is_me: Set this to True when you want this Character to represent
            the owner of the configuration under which the program is being run.
            A Character who is_me can do things that other Characters can't,
            like run servers, sign messages, and decrypt messages which are
            encrypted for them.  Typically this will be True for exactly one
            Character, but there are scenarios in which its imaginable to be
            represented by zero Characters or by more than one Character.

        """

        #
        # Prologue of the federation
        #

        # FIXME: excuse me... can I speak to the manager?
        if is_me:
            # If this is a federated-is_me-character, assume everyone else is too.
            self._set_known_node_class(known_node_class, federated_only)
        else:
            # What an awful hack.  The last convulsions of #466.  # TODO: Anything else.
            with suppress(AttributeError):
                federated_only = known_node_class._federated_only_instances

        if federated_only:
            if registry or provider_uri:
                raise ValueError(
                    f"Cannot init federated-only character with {registry or provider_uri}."
                )
        self.federated_only: bool = federated_only

        ##########################################

        #
        # Keys & Powers
        #

        if keyring:
            keyring_root, keyring_checksum_address = keyring.keyring_root, keyring.checksum_address
            if checksum_address and (keyring_checksum_address !=
                                     checksum_address):
                raise ValueError(
                    f"Provided checksum address {checksum_address} "
                    f"does not match character's keyring checksum address {keyring_checksum_address}"
                )
            checksum_address = keyring_checksum_address

            crypto_power_ups = list()
            for power_up in self._default_crypto_powerups:
                power = keyring.derive_crypto_power(power_class=power_up)
                crypto_power_ups.append(power)
        self.keyring = keyring

        if crypto_power and crypto_power_ups:
            raise ValueError(
                "Pass crypto_power or crypto_power_ups (or neither), but not both."
            )
        crypto_power_ups = crypto_power_ups or list()  # type: list

        if crypto_power:
            self._crypto_power = crypto_power  # type: CryptoPower
        elif crypto_power_ups:
            self._crypto_power = CryptoPower(power_ups=crypto_power_ups)
        else:
            self._crypto_power = CryptoPower(
                power_ups=self._default_crypto_powerups)

        #
        # Self
        #

        if is_me:

            # Signing Power
            self.signer = signer
            try:
                signing_power = self._crypto_power.power_ups(
                    SigningPower)  # type: SigningPower
                self._stamp = signing_power.get_signature_stamp(
                )  # type: SignatureStamp
            except NoSigningPower:
                self._stamp = NO_SIGNING_POWER

            # Blockchainy
            if not self.federated_only:
                self.provider_uri = provider_uri

                # TODO: Implicit / lazy blockchain connection here?
                # if not BlockchainInterfaceFactory.is_interface_initialized(provider_uri=provider_uri):
                #     BlockchainInterfaceFactory.initialize_interface(provider_uri=provider_uri)

                self.registry = registry or InMemoryContractRegistry.from_latest_publication(
                    network=domain)  # See #1580
            else:
                self.registry = NO_BLOCKCHAIN_CONNECTION.bool_value(False)

            # REST
            self.network_middleware = network_middleware or RestMiddleware(
                registry=self.registry)

            # Learner
            Learner.__init__(
                self,
                domain=domain,
                network_middleware=self.network_middleware,
                node_class=known_node_class,
                include_self_in_the_state=include_self_in_the_state,
                *args,
                **kwargs)

            if self.federated_only:
                try:
                    derived_federated_address = self.derive_federated_address()
                except NoSigningPower:
                    derived_federated_address = NO_SIGNING_POWER.bool_value(
                        False)

                if checksum_address and (checksum_address !=
                                         derived_federated_address):
                    raise ValueError(
                        f"Provided checksum address {checksum_address} "
                        f"does not match federated character's verifying key {derived_federated_address}"
                    )
                checksum_address = derived_federated_address

            self.checksum_address = checksum_address

        #
        # Stranger
        #

        else:
            if network_middleware is not None:
                raise TypeError(
                    "Network middleware cannot be attached to a Stranger-Character."
                )

            if registry is not None:
                raise TypeError(
                    "Registry cannot be attached to stranger-Characters.")

            verifying_key = self.public_keys(SigningPower)
            self._stamp = StrangerStamp(verifying_key)
            self.keyring_root = STRANGER
            self.network_middleware = STRANGER
            self.checksum_address = checksum_address

        self.__setup_nickname(is_me=is_me)

        # Character Control
        # TODO: have argument about meaning of 'lawful' and whether maybe only Lawful characters have an interface
        if hasattr(self, '_interface_class'):
            # Controller Interface
            self.interface = self._interface_class(character=self)
        self.controller = NO_CONTROL_PROTOCOL
Esempio n. 7
0
    def __init__(self,
                 domains: Set = None,
                 is_me: bool = True,
                 federated_only: bool = False,
                 checksum_address: str = NO_BLOCKCHAIN_CONNECTION.bool_value(False),
                 network_middleware: RestMiddleware = None,
                 keyring: NucypherKeyring = None,
                 keyring_root: str = None,
                 crypto_power: CryptoPower = None,
                 crypto_power_ups: List[CryptoPowerUp] = None,
                 provider_uri: str = None,
                 registry: BaseContractRegistry = None,
                 *args, **kwargs
                 ) -> None:

        """

        Base class for Nucypher protocol actors.


        PowerUps
        ========
        :param crypto_power: A CryptoPower object; if provided, this will be the character's CryptoPower.
        :param crypto_power_ups: If crypto_power is not provided, a new one will be made to consume all CryptoPowerUps.

        If neither crypto_power nor crypto_power_ups are provided, we give this
        Character all CryptoPowerUps listed in their _default_crypto_powerups
        attribute.

        :param is_me: Set this to True when you want this Character to represent
            the owner of the configuration under which the program is being run.
            A Character who is_me can do things that other Characters can't,
            like run servers, sign messages, and decrypt messages which are
            encrypted for them.  Typically this will be True for exactly one
            Character, but there are scenarios in which its imaginable to be
            represented by zero Characters or by more than one Character.

        """

        #
        # Operating Mode
        #
        if federated_only:
            if registry or provider_uri:
                raise ValueError(f"Cannot init federated-only character with {registry or provider_uri}.")
        self.federated_only = federated_only  # type: bool

        #
        # Powers
        #

        # Derive powers from keyring
        if keyring_root and keyring:
            if keyring_root != keyring.keyring_root:
                raise ValueError("Inconsistent keyring root directory path")
        if keyring:
            keyring_root, checksum_address = keyring.keyring_root, keyring.checksum_address
            crypto_power_ups = list()
            for power_up in self._default_crypto_powerups:
                power = keyring.derive_crypto_power(power_class=power_up)
                crypto_power_ups.append(power)
        self.keyring_root = keyring_root
        self.keyring = keyring

        if crypto_power and crypto_power_ups:
            raise ValueError("Pass crypto_power or crypto_power_ups (or neither), but not both.")
        crypto_power_ups = crypto_power_ups or list()  # type: list

        if crypto_power:
            self._crypto_power = crypto_power  # type: CryptoPower
        elif crypto_power_ups:
            self._crypto_power = CryptoPower(power_ups=crypto_power_ups)
        else:
            self._crypto_power = CryptoPower(power_ups=self._default_crypto_powerups)

        self._checksum_address = checksum_address

        # Fleet and Blockchain Connection (Everyone)
        if not domains:
            domains = (CharacterConfiguration.DEFAULT_DOMAIN,)

        #
        # Self-Character
        #

        if is_me:
            if not bool(federated_only) ^ bool(registry):
                raise ValueError(f"Pass either federated only or registry for is_me Characters.  \
                                 Got '{federated_only}' and '{registry}'.")

            self.treasure_maps = {}  # type: dict
            self.network_middleware = network_middleware or RestMiddleware()

            #
            # Signing Power
            #
            try:
                signing_power = self._crypto_power.power_ups(SigningPower)  # type: SigningPower
                self._stamp = signing_power.get_signature_stamp()  # type: SignatureStamp
            except NoSigningPower:
                self._stamp = NO_SIGNING_POWER

            #
            # Blockchain
            #
            self.provider_uri = provider_uri
            if not self.federated_only:
                self.registry = registry or InMemoryContractRegistry.from_latest_publication()
            else:
                self.registry = NO_BLOCKCHAIN_CONNECTION.bool_value(False)

            #
            # Learner
            #
            Learner.__init__(self,
                             domains=domains,
                             network_middleware=self.network_middleware,
                             *args, **kwargs)

        #
        # Stranger-Character
        #

        else:  # Feel like a stranger
            if network_middleware is not None:
                raise TypeError("Network middleware cannot be attached to a Stranger-Character.")

            if registry is not None:
                raise TypeError("Registry cannot be attached to stranger-Characters.")

            self._stamp = StrangerStamp(self.public_keys(SigningPower))
            self.network_middleware = STRANGER

        #
        # Decentralized
        #
        if not federated_only:
            self._checksum_address = checksum_address  # TODO: Check that this matches TransactingPower

        #
        # Federated
        #
        elif federated_only:
            try:
                self._set_checksum_address()  # type: str
            except NoSigningPower:
                self._checksum_address = NO_BLOCKCHAIN_CONNECTION
            if checksum_address:
                # We'll take a checksum address, as long as it matches their singing key
                if not checksum_address == self.checksum_address:
                    error = "Federated-only Characters derive their address from their Signing key; got {} instead."
                    raise self.SuspiciousActivity(error.format(checksum_address))

        #
        # Nicknames
        #
        try:
            self.nickname, self.nickname_metadata = nickname_from_seed(self.checksum_address)
        except SigningPower.not_found_error:
            if self.federated_only:
                self.nickname = self.nickname_metadata = NO_NICKNAME
            else:
                raise

        #
        # Fleet state
        #
        if is_me is True:
            self.known_nodes.record_fleet_state()

        #
        # Character Control
        #
        self.controller = NO_CONTROL_PROTOCOL
Esempio n. 8
0
    def __init__(self,
                 domain: str = None,
                 known_node_class: object = None,
                 is_me: bool = True,
                 federated_only: bool = False,
                 checksum_address: str = NO_BLOCKCHAIN_CONNECTION.bool_value(False),
                 network_middleware: RestMiddleware = None,
                 keyring: NucypherKeyring = None,
                 keyring_root: str = None,
                 crypto_power: CryptoPower = None,
                 crypto_power_ups: List[CryptoPowerUp] = None,
                 provider_uri: str = None,
                 signer: Signer = None,
                 registry: BaseContractRegistry = None,
                 *args, **kwargs
                 ) -> None:

        """

        A participant in the cryptological drama (a screenplay, if you like) of NuCypher.

        Characters can represent users, nodes, wallets, offline devices, or other objects of varying levels of abstraction.

        The Named Characters use this class as a Base, and achieve their individuality from additional methods and PowerUps.


        PowerUps
        ========
        :param crypto_power: A CryptoPower object; if provided, this will be the character's CryptoPower.
        :param crypto_power_ups: If crypto_power is not provided, a new one will be made to consume all CryptoPowerUps.

        If neither crypto_power nor crypto_power_ups are provided, we give this
        Character all CryptoPowerUps listed in their _default_crypto_powerups
        attribute.

        :param is_me: Set this to True when you want this Character to represent
            the owner of the configuration under which the program is being run.
            A Character who is_me can do things that other Characters can't,
            like run servers, sign messages, and decrypt messages which are
            encrypted for them.  Typically this will be True for exactly one
            Character, but there are scenarios in which its imaginable to be
            represented by zero Characters or by more than one Character.

        """

        if provider_uri:
            if not BlockchainInterfaceFactory.is_interface_initialized(provider_uri=provider_uri):
                BlockchainInterfaceFactory.initialize_interface(provider_uri=provider_uri)

        #
        # Operating Mode
        #

        if hasattr(self, '_interface_class'):  # TODO: have argument about meaning of 'lawful'
            #                                         and whether maybe only Lawful characters have an interface
            self.interface = self._interface_class(character=self)

        if is_me:
            self._set_known_node_class(known_node_class, federated_only)
        else:
            # What an awful hack.  The last convulsions of #466.
            # TODO: Anything else.
            with suppress(AttributeError):
                federated_only = known_node_class._federated_only_instances

        if federated_only:
            if registry or provider_uri:
                raise ValueError(f"Cannot init federated-only character with {registry or provider_uri}.")
        self.federated_only: bool = federated_only

        #
        # Powers
        #

        # Derive powers from keyring
        if keyring_root and keyring:
            if keyring_root != keyring.keyring_root:
                raise ValueError("Inconsistent keyring root directory path")
        if keyring:
            keyring_root, checksum_address = keyring.keyring_root, keyring.checksum_address
            crypto_power_ups = list()
            for power_up in self._default_crypto_powerups:
                power = keyring.derive_crypto_power(power_class=power_up)
                crypto_power_ups.append(power)
        self.keyring_root = keyring_root
        self.keyring = keyring

        if crypto_power and crypto_power_ups:
            raise ValueError("Pass crypto_power or crypto_power_ups (or neither), but not both.")
        crypto_power_ups = crypto_power_ups or list()  # type: list

        if crypto_power:
            self._crypto_power = crypto_power  # type: CryptoPower
        elif crypto_power_ups:
            self._crypto_power = CryptoPower(power_ups=crypto_power_ups)
        else:
            self._crypto_power = CryptoPower(power_ups=self._default_crypto_powerups)

        #
        # Self-Character
        #

        if is_me:
            #
            # Signing Power
            #
            self.signer = signer
            try:
                signing_power = self._crypto_power.power_ups(SigningPower)  # type: SigningPower
                self._stamp = signing_power.get_signature_stamp()  # type: SignatureStamp
            except NoSigningPower:
                self._stamp = NO_SIGNING_POWER

            #
            # Blockchain
            #
            self.provider_uri = provider_uri
            if not self.federated_only:
                self.registry = registry or InMemoryContractRegistry.from_latest_publication(network=domain)  # See #1580
            else:
                self.registry = NO_BLOCKCHAIN_CONNECTION.bool_value(False)

            # REST
            self.network_middleware = network_middleware or RestMiddleware(registry=self.registry)

            #
            # Learner
            #
            Learner.__init__(self,
                             domain=domain,
                             network_middleware=self.network_middleware,
                             node_class=known_node_class,
                             *args, **kwargs)

        #
        # Stranger-Character
        #

        else:  # Feel like a stranger
            if network_middleware is not None:
                raise TypeError("Network middleware cannot be attached to a Stranger-Character.")

            if registry is not None:
                raise TypeError("Registry cannot be attached to stranger-Characters.")

            verifying_key = self.public_keys(SigningPower)
            self._stamp = StrangerStamp(verifying_key)
            self.keyring_root = STRANGER
            self.network_middleware = STRANGER

        # TODO: Figure out when to do this.
        try:
            _transacting_power = self._crypto_power.power_ups(TransactingPower)
        except NoTransactingPower:
            self._checksum_address = checksum_address
        else:
            self._set_checksum_address(checksum_address)

        #
        # Nicknames
        #
        if self._checksum_address is NO_BLOCKCHAIN_CONNECTION and not self.federated_only and not is_me:
            # Sometimes we don't care about the nickname.  For example, if Alice is granting to Bob, she usually
            # doesn't know or care about his wallet.  Maybe this needs to change?
            # Currently, if this is a stranger and there's no blockchain connection, we assign NO_NICKNAME:
            self.nickname = NO_NICKNAME
        else:
            try:
                # TODO: It's possible that this is NO_BLOCKCHAIN_CONNECTION.
                if self.checksum_address is NO_BLOCKCHAIN_CONNECTION:
                    self.nickname = NO_NICKNAME
                else:
                    # This can call _set_checksum_address.
                    self.nickname = Nickname.from_seed(self.checksum_address)
            except SigningPower.not_found_error:  # TODO: Handle NO_BLOCKCHAIN_CONNECTION more coherently - #1547
                if self.federated_only:
                    self.nickname = NO_NICKNAME
                else:
                    raise

        #
        # Fleet state
        #
        if is_me is True:
            self.known_nodes.record_fleet_state()

        #
        # Character Control
        #
        self.controller = NO_CONTROL_PROTOCOL
Esempio n. 9
0
    def __init__(
            self,

            # Base
            config_root: str = None,
            filepath: str = None,

            # Mode
            dev_mode: bool = False,
            federated_only: bool = False,

            # Identity
            checksum_address: str = None,
            crypto_power: CryptoPower = None,

            # Keyring
            keyring: NucypherKeyring = None,
            keyring_root: str = None,

            # Learner
            learn_on_same_thread: bool = False,
            abort_on_learning_error: bool = False,
            start_learning_now: bool = True,

            # Network
            controller_port: int = None,
            domains: Set[str] = None,
            interface_signature: Signature = None,
            network_middleware: RestMiddleware = None,

            # Node Storage
            known_nodes: set = None,
            node_storage: NodeStorage = None,
            reload_metadata: bool = True,
            save_metadata: bool = True,

            # Blockchain
            poa: bool = False,
            provider_uri: str = None,
            provider_process=None,

            # Registry
            registry_filepath: str = None,
            download_registry: bool = True) -> None:

        self.log = Logger(self.__class__.__name__)

        # Identity
        # NOTE: NodeConfigurations can only be used with Self-Characters
        self.is_me = True
        self.checksum_address = checksum_address

        # Network
        self.controller_port = controller_port or self.DEFAULT_CONTROLLER_PORT
        self.network_middleware = network_middleware or self.DEFAULT_NETWORK_MIDDLEWARE(
        )
        self.interface_signature = interface_signature

        # Keyring
        self.crypto_power = crypto_power
        self.keyring = keyring or NO_KEYRING_ATTACHED
        self.keyring_root = keyring_root or UNINITIALIZED_CONFIGURATION

        # Contract Registry
        self.download_registry = download_registry
        self.registry_filepath = registry_filepath or UNINITIALIZED_CONFIGURATION

        # Blockchain
        self.poa = poa
        self.provider_uri = provider_uri or self.DEFAULT_PROVIDER_URI
        self.provider_process = provider_process or NO_BLOCKCHAIN_CONNECTION
        self.blockchain = NO_BLOCKCHAIN_CONNECTION.bool_value(False)
        self.token_agent = NO_BLOCKCHAIN_CONNECTION
        self.staking_agent = NO_BLOCKCHAIN_CONNECTION
        self.policy_agent = NO_BLOCKCHAIN_CONNECTION

        # Learner
        self.federated_only = federated_only
        self.domains = domains or {self.DEFAULT_DOMAIN}
        self.learn_on_same_thread = learn_on_same_thread
        self.abort_on_learning_error = abort_on_learning_error
        self.start_learning_now = start_learning_now
        self.save_metadata = save_metadata
        self.reload_metadata = reload_metadata
        self.__known_nodes = known_nodes or set()  # handpicked
        self.__fleet_state = FleetStateTracker()

        # Configuration
        self.__dev_mode = dev_mode
        self.config_file_location = filepath or UNINITIALIZED_CONFIGURATION
        self.config_root = UNINITIALIZED_CONFIGURATION

        if dev_mode:
            self.__temp_dir = UNINITIALIZED_CONFIGURATION
            self.__setup_node_storage()
            self.initialize(password=DEVELOPMENT_CONFIGURATION)
        else:
            self.__temp_dir = LIVE_CONFIGURATION
            self.config_root = config_root or self.DEFAULT_CONFIG_ROOT
            self._cache_runtime_filepaths()
            self.__setup_node_storage(node_storage=node_storage)

        super().__init__(filepath=self.config_file_location,
                         config_root=self.config_root)
Esempio n. 10
0
    def __init__(
            self,

            # Base
            emitter=None,
            config_root: Optional[Path] = None,
            filepath: Optional[Path] = None,

            # Mode
            dev_mode: bool = False,
            federated_only: bool = False,

            # Identity
            checksum_address: str = None,
            crypto_power: CryptoPower = None,

            # Keystore
            keystore: Keystore = None,
            keystore_path: Optional[Path] = None,

            # Learner
            learn_on_same_thread: bool = False,
            abort_on_learning_error: bool = False,
            start_learning_now: bool = True,

            # Network
            controller_port: int = None,
            domain: str = DEFAULT_DOMAIN,
            network_middleware: RestMiddleware = None,
            lonely: bool = False,

            # Node Storage
            known_nodes: set = None,
            node_storage: NodeStorage = None,
            reload_metadata: bool = True,
            save_metadata: bool = True,

            # Blockchain
            poa: bool = None,
            light: bool = False,
            provider_uri: str = None,
            gas_strategy: Union[Callable, str] = DEFAULT_GAS_STRATEGY,
            max_gas_price: Optional[int] = None,
            signer_uri: str = None,

            # Registry
            registry: BaseContractRegistry = None,
            registry_filepath: Optional[Path] = None,

            # Deployed Workers
            worker_data: dict = None):

        self.log = Logger(self.__class__.__name__)

        # This constant is used to signal that a path can be generated if one is not provided.
        UNINITIALIZED_CONFIGURATION.bool_value(False)

        # Identity
        # NOTE: NodeConfigurations can only be used with Self-Characters
        self.is_me = True
        self.checksum_address = checksum_address

        # Keystore
        self.crypto_power = crypto_power
        if keystore_path and not keystore:
            keystore = Keystore(keystore_path=keystore_path)
        self.__keystore = self.__keystore = keystore or NO_KEYSTORE_ATTACHED.bool_value(
            False)
        self.keystore_dir = Path(
            keystore.keystore_path
        ).parent if keystore else UNINITIALIZED_CONFIGURATION

        # Contract Registry
        if registry and registry_filepath:
            if registry.filepath != registry_filepath:
                error = f"Inconsistent registry filepaths for '{registry.filepath.absolute()}'" \
                        f" and '{registry_filepath.absolute()}'."
                raise ValueError(error)
            else:
                self.log.warn(
                    f"Registry and registry filepath were both passed.")
        self.registry = registry or NO_BLOCKCHAIN_CONNECTION.bool_value(False)
        self.registry_filepath = registry_filepath or UNINITIALIZED_CONFIGURATION

        # Blockchain
        self.poa = poa
        self.is_light = light
        self.provider_uri = provider_uri or NO_BLOCKCHAIN_CONNECTION
        self.signer_uri = signer_uri or None

        # Learner
        self.federated_only = federated_only
        self.domain = domain
        self.learn_on_same_thread = learn_on_same_thread
        self.abort_on_learning_error = abort_on_learning_error
        self.start_learning_now = start_learning_now
        self.save_metadata = save_metadata
        self.reload_metadata = reload_metadata
        self.known_nodes = known_nodes or set()  # handpicked
        self.lonely = lonely

        # Configuration
        self.__dev_mode = dev_mode
        self.config_file_location = filepath or UNINITIALIZED_CONFIGURATION
        self.config_root = UNINITIALIZED_CONFIGURATION

        # Deployed Workers
        self.worker_data = worker_data

        #
        # Federated vs. Blockchain arguments consistency
        #

        #
        # Federated
        #

        if self.federated_only:
            # Check for incompatible values
            blockchain_args = {
                'filepath': registry_filepath,
                'poa': poa,
                'provider_uri': provider_uri,
                'gas_strategy': gas_strategy,
                'max_gas_price': max_gas_price
            }
            if any(blockchain_args.values()):
                bad_args = ", ".join(f"{arg}={val}"
                                     for arg, val in blockchain_args.items()
                                     if val)
                self.log.warn(
                    f"Arguments {bad_args} are incompatible with federated_only. "
                    f"Overridden with a sane default.")

                # Clear decentralized attributes to ensure consistency with a
                # federated configuration.
                self.poa = False
                self.is_light = False
                self.provider_uri = None
                self.registry_filepath = None
                self.gas_strategy = None
                self.max_gas_price = None

        #
        # Decentralized
        #

        else:
            self.gas_strategy = gas_strategy
            self.max_gas_price = max_gas_price  # gwei
            is_initialized = BlockchainInterfaceFactory.is_interface_initialized(
                provider_uri=self.provider_uri)
            if not is_initialized and provider_uri:
                BlockchainInterfaceFactory.initialize_interface(
                    provider_uri=self.provider_uri,
                    poa=self.poa,
                    light=self.is_light,
                    emitter=emitter,
                    gas_strategy=self.gas_strategy,
                    max_gas_price=self.max_gas_price)
            else:
                self.log.warn(
                    f"Using existing blockchain interface connection ({self.provider_uri})."
                )

            if not self.registry:
                # TODO: These two code blocks are untested.
                if not self.registry_filepath:  # TODO: Registry URI  (goerli://speedynet.json) :-)
                    self.log.info(f"Fetching latest registry from source.")
                    self.registry = InMemoryContractRegistry.from_latest_publication(
                        network=self.domain)
                else:
                    self.registry = LocalContractRegistry(
                        filepath=self.registry_filepath)
                    self.log.info(f"Using local registry ({self.registry}).")

            self.testnet = self.domain != NetworksInventory.MAINNET
            self.signer = Signer.from_signer_uri(self.signer_uri,
                                                 testnet=self.testnet)

        if dev_mode:
            self.__temp_dir = UNINITIALIZED_CONFIGURATION
            self._setup_node_storage()
            self.initialize(password=DEVELOPMENT_CONFIGURATION)
        else:
            self.__temp_dir = LIVE_CONFIGURATION
            self.config_root = config_root or self.DEFAULT_CONFIG_ROOT
            self._cache_runtime_filepaths()
            self._setup_node_storage(node_storage=node_storage)

        # Network
        self.controller_port = controller_port or self.DEFAULT_CONTROLLER_PORT
        self.network_middleware = network_middleware or self.DEFAULT_NETWORK_MIDDLEWARE(
            registry=self.registry)

        super().__init__(filepath=self.config_file_location,
                         config_root=self.config_root)
Esempio n. 11
0
class BlockchainInterface:
    """
    Interacts with a solidity compiler and a registry in order to instantiate compiled
    ethereum contracts with the given web3 provider backend.
    """

    TIMEOUT = 180  # seconds
    NULL_ADDRESS = '0x' + '0' * 40

    _instance = NO_BLOCKCHAIN_CONNECTION.bool_value(False)
    process = NO_PROVIDER_PROCESS.bool_value(False)
    Web3 = Web3

    _contract_factory = Contract

    class InterfaceError(Exception):
        pass

    class NoProvider(InterfaceError):
        pass

    class ConnectionFailed(InterfaceError):
        pass

    class UnknownContract(InterfaceError):
        pass

    def __init__(self,
                 poa: bool = True,
                 provider_process: NuCypherGethProcess = NO_PROVIDER_PROCESS,
                 provider_uri: str = NO_BLOCKCHAIN_CONNECTION,
                 transacting_power: TransactingPower = READ_ONLY_INTERFACE,
                 provider: Web3Providers = NO_BLOCKCHAIN_CONNECTION,
                 registry: EthereumContractRegistry = None):
        """
        A blockchain "network interface"; The circumflex wraps entirely around the bounds of
        contract operations including compilation, deployment, and execution.

         Filesystem          Configuration           Node              Client                  EVM
        ================ ====================== =============== =====================  ===========================

         Solidity Files -- SolidityCompiler ---                  --- HTTPProvider ------ ...
                                               |                |
                                               |                |

                                                 *Blockchain* -- IPCProvider ----- External EVM (geth, parity...)

                                               |      |         |
                                               |      |         |
         Registry File -- ContractRegistry ---        |          ---- TestProvider ----- EthereumTester
                                                      |
                        |                             |                                         |
                        |                             |
                                                                                        PyEVM (Development Chain)
         Runtime Files --                 -------- Blockchain
                                         |
                        |                |             |

         Key Files ------ CharacterConfiguration -------- Agent ... (Contract API)

                        |                |             |
                        |                |
                        |                 ---------- Actor ... (Blockchain-Character API)
                        |
                        |                              |
                        |
         Config File ---                           Character ... (Public API)

                                                       |

                                                     Human


        The Blockchain is the junction of the solidity compiler, a contract registry, and a collection of
        web3 network providers as a means of interfacing with the ethereum blockchain to execute
        or deploy contract code on the network.


        Compiler and Registry Usage
        -----------------------------

        Contracts are freshly re-compiled if an instance of SolidityCompiler is passed; otherwise,
        The registry will read contract data saved to disk that is be used to retrieve contact address and op-codes.
        Optionally, A registry instance can be passed instead.


        Provider Usage
        ---------------
        https: // github.com / ethereum / eth - tester     # available-backends


        * HTTP Provider - Web3 HTTP provider, typically JSON RPC 2.0 over HTTP
        * Websocket Provider - Web3 WS provider, typically JSON RPC 2.0 over WS, supply endpoint uri and websocket=True
        * IPC Provider - Web3 File based IPC provider transported over standard I/O
        * Custom Provider - A pre-initialized web3.py provider instance to attach to this interface

        """

        self.log = Logger('Blockchain')
        self.poa = poa
        self.provider_uri = provider_uri
        self._provider = provider
        self._provider_process = provider_process
        self.client = NO_BLOCKCHAIN_CONNECTION
        self.transacting_power = transacting_power
        self.registry = registry
        BlockchainInterface._instance = self

    def __repr__(self):
        r = '{name}({uri})'.format(name=self.__class__.__name__,
                                   uri=self.provider_uri)
        return r

    @classmethod
    def from_dict(cls, payload: dict, **overrides) -> 'BlockchainInterface':

        # Apply overrides
        payload.update({k: v for k, v in overrides.items() if v is not None})

        registry = EthereumContractRegistry(
            registry_filepath=payload['registry_filepath'])
        blockchain = cls(provider_uri=payload['provider_uri'],
                         registry=registry)
        return blockchain

    def to_dict(self) -> dict:
        payload = dict(provider_uri=self.provider_uri,
                       poa=self.poa,
                       registry_filepath=self.registry.filepath)
        return payload

    def _configure_registry(self, fetch_registry: bool = True) -> None:
        RegistryClass = EthereumContractRegistry._get_registry_class(
            local=self.client.is_local)
        if fetch_registry:
            registry = RegistryClass.from_latest_publication()
        else:
            registry = RegistryClass()
        self.registry = registry
        self.log.info("Using contract registry {}".format(
            self.registry.filepath))

    @property
    def is_connected(self) -> bool:
        """
        https://web3py.readthedocs.io/en/stable/__provider.html#examples-using-automated-detection
        """
        if self.client is NO_BLOCKCHAIN_CONNECTION:
            return False
        return self.client.is_connected

    def disconnect(self) -> None:
        if self._provider_process:
            self._provider_process.stop()
        self._provider_process = NO_PROVIDER_PROCESS
        self._provider = NO_BLOCKCHAIN_CONNECTION
        BlockchainInterface._instance = NO_BLOCKCHAIN_CONNECTION

    @classmethod
    def reconnect(cls, *args, **kwargs) -> 'BlockchainInterface':
        return cls._instance

    def attach_middleware(self):

        # For use with Proof-Of-Authority test-blockchains
        if self.poa is True:
            self.log.debug('Injecting POA middleware at layer 0')
            self.client.inject_middleware(geth_poa_middleware, layer=0)

    def connect(self, fetch_registry: bool = True, sync_now: bool = False):

        # Spawn child process
        if self._provider_process:
            self._provider_process.start()
            provider_uri = self._provider_process.provider_uri(scheme='file')
        else:
            provider_uri = self.provider_uri
            self.log.info(
                f"Using external Web3 Provider '{self.provider_uri}'")

        # Attach Provider
        self._attach_provider(provider=self._provider,
                              provider_uri=provider_uri)
        self.log.info("Connecting to {}".format(self.provider_uri))
        if self._provider is NO_BLOCKCHAIN_CONNECTION:
            raise self.NoProvider(
                "There are no configured blockchain providers")

        # Connect Web3 Instance
        try:
            self.w3 = self.Web3(provider=self._provider)
            self.client = Web3Client.from_w3(w3=self.w3)
        except requests.ConnectionError:  # RPC
            raise self.ConnectionFailed(
                f'Connection Failed - {str(self.provider_uri)} - is RPC enabled?'
            )
        except FileNotFoundError:  # IPC File Protocol
            raise self.ConnectionFailed(
                f'Connection Failed - {str(self.provider_uri)} - is IPC enabled?'
            )
        else:
            self.attach_middleware()

        # Wait for chaindata sync
        if sync_now:
            self.client.sync()

        # Establish contact with NuCypher contracts
        if not self.registry:
            self._configure_registry(fetch_registry=fetch_registry)

        return self.is_connected

    @property
    def provider(self) -> Union[IPCProvider, WebsocketProvider, HTTPProvider]:
        return self._provider

    def _attach_provider(self,
                         provider: Web3Providers = None,
                         provider_uri: str = None) -> None:
        """
        https://web3py.readthedocs.io/en/latest/providers.html#providers
        """

        if not provider_uri and not provider:
            raise self.NoProvider("No URI or provider instances supplied.")

        if provider_uri and not provider:
            uri_breakdown = urlparse(provider_uri)

            if uri_breakdown.scheme == 'tester':
                providers = {
                    'pyevm': _get_tester_pyevm,
                    'geth': _get_test_geth_parity_provider,
                    'parity-ethereum': _get_test_geth_parity_provider,
                }
                provider_scheme = uri_breakdown.netloc
            else:
                providers = {
                    'auto': _get_auto_provider,
                    'infura': _get_infura_provider,
                    'ipc': _get_IPC_provider,
                    'file': _get_IPC_provider,
                    'ws': _get_websocket_provider,
                    'http': _get_HTTP_provider,
                    'https': _get_HTTP_provider,
                }
                provider_scheme = uri_breakdown.scheme
            try:
                self._provider = providers[provider_scheme](provider_uri)
            except KeyError:
                raise ValueError(
                    f"{provider_uri} is an invalid or unsupported blockchain provider URI"
                )
            else:
                self.provider_uri = provider_uri or NO_BLOCKCHAIN_CONNECTION
        else:
            self._provider = provider

    def send_transaction(
        self,
        contract_function: ContractFunction,
        sender_address: str,
        payload: dict = None,
    ) -> dict:

        if self.transacting_power is READ_ONLY_INTERFACE:
            raise self.InterfaceError(str(READ_ONLY_INTERFACE))

        #
        # Build
        #

        if not payload:
            payload = {}

        nonce = self.client.w3.eth.getTransactionCount(sender_address)
        payload.update({
            'chainId': int(self.client.net_version),
            'nonce': nonce,
            'from': sender_address,
            'gasPrice': self.client.gas_price,
            # 'gas': 0,  # TODO: Gas Management
        })

        # Get interface name
        deployment = True if isinstance(contract_function,
                                        ContractConstructor) else False

        try:
            transaction_name = contract_function.fn_name.upper()
        except AttributeError:
            if deployment:
                transaction_name = 'DEPLOY'
            else:
                transaction_name = 'UNKNOWN'

        payload_pprint = dict(payload)
        payload_pprint['from'] = to_checksum_address(payload['from'])
        payload_pprint = ', '.join("{}: {}".format(k, v)
                                   for k, v in payload_pprint.items())
        self.log.debug(f"[TX-{transaction_name}] | {payload_pprint}")

        # Build transaction payload
        try:
            unsigned_transaction = contract_function.buildTransaction(payload)
        except ValidationError as e:
            # TODO: Handle validation failures for gas limits, invalid fields, etc.
            self.log.warn(f"Validation error: {e}")
            raise
        else:
            if deployment:
                self.log.info(
                    f"Deploying contract: {len(unsigned_transaction['data'])} bytes"
                )

        #
        # Broadcast
        #

        signed_raw_transaction = self.transacting_power.sign_transaction(
            unsigned_transaction)
        txhash = self.client.send_raw_transaction(signed_raw_transaction)

        try:
            receipt = self.client.wait_for_receipt(txhash,
                                                   timeout=self.TIMEOUT)
        except TimeExhausted:
            # TODO: Handle transaction timeout
            raise
        else:
            self.log.debug(
                f"[RECEIPT-{transaction_name}] | txhash: {receipt['transactionHash'].hex()}"
            )

        #
        # Confirm
        #

        # Primary check
        deployment_status = receipt.get('status', UNKNOWN_TX_STATUS)
        if deployment_status is 0:
            failure = f"Transaction transmitted, but receipt returned status code 0. " \
                      f"Full receipt: \n {pprint.pformat(receipt, indent=2)}"
            raise self.InterfaceError(failure)

        if deployment_status is UNKNOWN_TX_STATUS:
            self.log.info(
                f"Unknown transaction status for {txhash} (receipt did not contain a status field)"
            )

            # Secondary check TODO: Is this a sensible check?
            tx = self.client.get_transaction(txhash)
            if tx["gas"] == receipt["gasUsed"]:
                raise self.InterfaceError(
                    f"Transaction consumed 100% of transaction gas."
                    f"Full receipt: \n {pprint.pformat(receipt, indent=2)}")

        return receipt

    def get_contract_by_name(
            self,
            name: str,
            proxy_name: str = None,
            use_proxy_address: bool = True) -> Union[Contract, List[tuple]]:
        """
        Instantiate a deployed contract from registry data,
        and assimilate it with its proxy if it is upgradeable,
        or return all registered records if use_proxy_address is False.
        """
        target_contract_records = self.registry.search(contract_name=name)

        if not target_contract_records:
            raise self.UnknownContract(
                f"No such contract records with name {name}.")

        if proxy_name:  # It's upgradeable
            # Lookup proxies; Search for a published proxy that targets this contract record

            proxy_records = self.registry.search(contract_name=proxy_name)

            results = list()
            for proxy_name, proxy_addr, proxy_abi in proxy_records:
                proxy_contract = self.client.w3.eth.contract(
                    abi=proxy_abi,
                    address=proxy_addr,
                    ContractFactoryClass=self._contract_factory)

                # Read this dispatcher's target address from the blockchain
                proxy_live_target_address = proxy_contract.functions.target(
                ).call()
                for target_name, target_addr, target_abi in target_contract_records:

                    if target_addr == proxy_live_target_address:
                        if use_proxy_address:
                            pair = (proxy_addr, target_abi)
                        else:
                            pair = (proxy_live_target_address, target_abi)
                    else:
                        continue

                    results.append(pair)

            if len(results) > 1:
                address, abi = results[0]
                message = "Multiple {} deployments are targeting {}".format(
                    proxy_name, address)
                raise self.InterfaceError(message.format(name))

            else:
                selected_address, selected_abi = results[0]

        else:  # It's not upgradeable
            if len(target_contract_records) != 1:
                m = "Multiple records registered for non-upgradeable contract {}"
                raise self.InterfaceError(m.format(name))
            _target_contract_name, selected_address, selected_abi = target_contract_records[
                0]

        # Create the contract from selected sources
        unified_contract = self.client.w3.eth.contract(
            abi=selected_abi,
            address=selected_address,
            ContractFactoryClass=self._contract_factory)

        return unified_contract
Esempio n. 12
0
    def __init__(self,

                 # Base
                 config_root: str = None,
                 config_file_location: str = None,

                 # Mode
                 dev_mode: bool = False,
                 federated_only: bool = False,

                 # Identity
                 is_me: bool = True,
                 checksum_address: str = None,
                 crypto_power: CryptoPower = None,

                 # Keyring
                 keyring: NucypherKeyring = None,
                 keyring_dir: str = None,

                 # Learner
                 learn_on_same_thread: bool = False,
                 abort_on_learning_error: bool = False,
                 start_learning_now: bool = True,

                 # REST
                 rest_host: str = None,
                 rest_port: int = None,
                 controller_port: int = None,

                 # TLS
                 tls_curve: EllipticCurve = None,
                 certificate: Certificate = None,

                 # Network
                 domains: Set[str] = None,
                 interface_signature: Signature = None,
                 network_middleware: RestMiddleware = None,

                 # Node Storage
                 known_nodes: set = None,
                 node_storage: NodeStorage = None,
                 reload_metadata: bool = True,
                 save_metadata: bool = True,

                 # Blockchain
                 poa: bool = False,
                 provider_uri: str = None,
                 provider_process = None,

                 # Registry
                 registry_source: str = None,
                 registry_filepath: str = None,
                 download_registry: bool = True

                 ) -> None:

        # Logs
        self.log = Logger(self.__class__.__name__)

        #
        # REST + TLS + Web
        #
        self.controller_port = controller_port or self.DEFAULT_CONTROLLER_PORT
        self.rest_host = rest_host or self.DEFAULT_REST_HOST
        default_port = (self.DEFAULT_DEVELOPMENT_REST_PORT if dev_mode else self.DEFAULT_REST_PORT)
        self.rest_port = rest_port or default_port
        self.tls_curve = tls_curve or self.__DEFAULT_TLS_CURVE
        self.certificate = certificate

        self.interface_signature = interface_signature
        self.crypto_power = crypto_power

        #
        # Keyring
        #
        self.keyring = keyring or NO_KEYRING_ATTACHED
        self.keyring_dir = keyring_dir or UNINITIALIZED_CONFIGURATION

        # Contract Registry
        self.download_registry = download_registry
        self.__registry_source = registry_source or self.REGISTRY_SOURCE
        self.registry_filepath = registry_filepath or UNINITIALIZED_CONFIGURATION

        #
        # Configuration
        #
        self.config_file_location = config_file_location or UNINITIALIZED_CONFIGURATION
        self.config_root = UNINITIALIZED_CONFIGURATION

        #
        # Mode
        #
        self.federated_only = federated_only
        self.__dev_mode = dev_mode

        if self.__dev_mode:
            self.__temp_dir = UNINITIALIZED_CONFIGURATION
            self.node_storage = ForgetfulNodeStorage(federated_only=federated_only, character_class=self.__class__)
        else:
            self.__temp_dir = LIVE_CONFIGURATION
            self.config_root = config_root or DEFAULT_CONFIG_ROOT
            self._cache_runtime_filepaths()
            self.node_storage = node_storage or LocalFileBasedNodeStorage(federated_only=federated_only,
                                                                          config_root=self.config_root)

        # Domains
        self.domains = domains or {self.DEFAULT_DOMAIN}

        #
        # Identity
        #
        self.is_me = is_me
        self.checksum_address = checksum_address

        if self.is_me is True or dev_mode is True:
            # Self
            if self.checksum_address and dev_mode is False:
                self.attach_keyring()
            self.network_middleware = network_middleware or self.__DEFAULT_NETWORK_MIDDLEWARE_CLASS()

        else:
            # Stranger
            self.node_storage = STRANGER_CONFIGURATION
            self.keyring_dir = STRANGER_CONFIGURATION
            self.keyring = STRANGER_CONFIGURATION
            self.network_middleware = STRANGER_CONFIGURATION
            if network_middleware:
                raise self.ConfigurationError("Cannot configure a stranger to use network middleware.")

        #
        # Learner
        #
        self.learn_on_same_thread = learn_on_same_thread
        self.abort_on_learning_error = abort_on_learning_error
        self.start_learning_now = start_learning_now
        self.save_metadata = save_metadata
        self.reload_metadata = reload_metadata

        self.__fleet_state = FleetStateTracker()
        known_nodes = known_nodes or set()
        if known_nodes:
            self.known_nodes._nodes.update({node.checksum_address: node for node in known_nodes})
            self.known_nodes.record_fleet_state()  # TODO: Does this call need to be here?

        #
        # Blockchain
        #
        self.poa = poa
        self.provider_uri = provider_uri or self.DEFAULT_PROVIDER_URI
        self.provider_process = provider_process or NO_BLOCKCHAIN_CONNECTION

        self.blockchain = NO_BLOCKCHAIN_CONNECTION.bool_value(False)
        self.accounts = NO_BLOCKCHAIN_CONNECTION
        self.token_agent = NO_BLOCKCHAIN_CONNECTION
        self.miner_agent = NO_BLOCKCHAIN_CONNECTION
        self.policy_agent = NO_BLOCKCHAIN_CONNECTION

        #
        # Development Mode
        #

        if dev_mode:

            # Ephemeral dev settings
            self.abort_on_learning_error = True
            self.save_metadata = False
            self.reload_metadata = False

            # Generate one-time alphanumeric development password
            alphabet = string.ascii_letters + string.digits
            password = ''.join(secrets.choice(alphabet) for _ in range(32))

            # Auto-initialize
            self.initialize(password=password, download_registry=download_registry)