From 600decc8b41f34e6dabc957a1cb579d2fbbcc4ad Mon Sep 17 00:00:00 2001 From: Kieran Prasch Date: Sun, 28 Apr 2019 01:39:50 +0300 Subject: [PATCH] Dear Felix, Admit that ethereum nodes need to be started early --- nucypher/blockchain/eth/clients.py | 38 ++++++++++++++++++++++----- nucypher/blockchain/eth/interfaces.py | 2 +- nucypher/cli/characters/felix.py | 11 ++++---- nucypher/config/node.py | 12 +++++++-- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/nucypher/blockchain/eth/clients.py b/nucypher/blockchain/eth/clients.py index 04d24c17c..bb8596cf1 100644 --- a/nucypher/blockchain/eth/clients.py +++ b/nucypher/blockchain/eth/clients.py @@ -2,6 +2,7 @@ import json import os from constant_sorrow.constants import NOT_RUNNING +from eth_utils import to_checksum_address, is_checksum_address from geth import LoggingMixin from geth.accounts import ensure_account_exists, get_accounts, create_new_account from geth.chain import ( @@ -22,7 +23,7 @@ NUCYPHER_CHAIN_IDS = { class NuCypherGethProcess(BaseGethProcess, LoggingMixin): - IPC_PROTOCOL = 'ipc' + IPC_PROTOCOL = 'http' IPC_FILENAME = 'geth.ipc' VERBOSITY = 5 @@ -33,16 +34,25 @@ class NuCypherGethProcess(BaseGethProcess, LoggingMixin): self.log = Logger('nucypher-geth') @property - def provider_uri(self, scheme: str = None): + def provider_uri(self, scheme: str = None) -> str: if not scheme: scheme = self.IPC_PROTOCOL - uri = f"{scheme}://{self.ipc_path}" + if scheme == 'file': + location = self.ipc_path + elif scheme in ('http', 'ws'): + location = f'{self.rpc_host}:{self.rpc_port}' + else: + raise ValueError(f'{scheme} is an unknown ethereum node IPC protocol.') + + uri = f"{scheme}://{location}" return uri def start(self, timeout: int = 30): self.log.info("STARTING GETH NOW") super().start() - self.wait_for_ipc(timeout=timeout) + self.wait_for_ipc(timeout=timeout) # on for all nodes by default + if self.IPC_PROTOCOL == 'rpc': + self.wait_for_rpc(timeout=timeout) class NuCypherGethDevProcess(NuCypherGethProcess): @@ -65,6 +75,7 @@ class NuCypherGethDevProcess(NuCypherGethProcess): class NuCypherGethDevnetProcess(NuCypherGethProcess): + IPC_PROTOCOL = 'file' GENESIS_FILENAME = 'testnet_genesis.json' GENESIS_SOURCE_FILEPATH = os.path.join(DEPLOY_DIR, GENESIS_FILENAME) @@ -119,6 +130,15 @@ class NuCypherGethDevnetProcess(NuCypherGethProcess): super().__init__(geth_kwargs) + @classmethod + def get_accounts(cls, data_dir: str): + geth_kwargs = {'network_id': str(cls.__CHAIN_ID), + 'port': str(cls.P2P_PORT), + 'verbosity': str(cls.VERBOSITY), + 'data_dir': data_dir} + accounts = get_accounts(**geth_kwargs) + return accounts + @classmethod def initialize_blockchain(cls, geth_kwargs: dict, overwrite: bool = True) -> None: log = Logger('nucypher-geth-init') @@ -126,11 +146,12 @@ class NuCypherGethDevnetProcess(NuCypherGethProcess): genesis_data = json.loads(file.read()) log.info(f"Read genesis file '{cls.GENESIS_SOURCE_FILEPATH}'") + genesis_data.update(dict(overwrite=overwrite)) log.info(f'Initializing new blockchain database and genesis block.') - initialize_chain(genesis_data, overwrite=overwrite, **geth_kwargs) + initialize_chain(genesis_data=genesis_data, **geth_kwargs) @classmethod - def ensure_account_exists(cls, password: str, data_dir: str): + def ensure_account_exists(cls, password: str, data_dir: str) -> str: geth_kwargs = {'network_id': str(cls.__CHAIN_ID), 'port': str(cls.P2P_PORT), 'verbosity': str(cls.VERBOSITY), @@ -141,4 +162,7 @@ class NuCypherGethDevnetProcess(NuCypherGethProcess): account = create_new_account(password=password.encode(), **geth_kwargs) else: account = accounts[0] - return account + + checksum_address = to_checksum_address(account.decode()) + assert is_checksum_address(checksum_address), f"GETH RETURNED INVALID ETH ADDRESS {checksum_address}" + return checksum_address diff --git a/nucypher/blockchain/eth/interfaces.py b/nucypher/blockchain/eth/interfaces.py index ec2a0415c..07a7fc995 100644 --- a/nucypher/blockchain/eth/interfaces.py +++ b/nucypher/blockchain/eth/interfaces.py @@ -330,7 +330,7 @@ class BlockchainInterface: # Built-In # - IPC - - elif uri_breakdown.scheme == 'ipc': + elif uri_breakdown.scheme in ('ipc', 'file'): # https://web3py.readthedocs.io/en/latest/providers.html#ipcprovider provider = IPCProvider(ipc_path=uri_breakdown.path, timeout=self.timeout) diff --git a/nucypher/cli/characters/felix.py b/nucypher/cli/characters/felix.py index 3294f66bd..82748bd1e 100644 --- a/nucypher/cli/characters/felix.py +++ b/nucypher/cli/characters/felix.py @@ -58,6 +58,12 @@ def felix(click_config, if not click_config.quiet: click.secho(FELIX_BANNER.format(checksum_address or '')) + ETH_NODE = None # TODO: Make constant + if geth: + ETH_NODE = NuCypherGethDevnetProcess(config_root=config_root) + ETH_NODE.start() + provider_uri = ETH_NODE.provider_uri + if action == "init": """Create a brand-new Felix""" @@ -92,11 +98,6 @@ def felix(click_config, # Domains -> bytes | or default domains = [bytes(network, encoding='utf-8')] if network else None - ETH_NODE = None - if geth: - ETH_NODE = NuCypherGethDevnetProcess(config_root=config_root) - ETH_NODE.start() - # Load Felix from Configuration File with overrides try: felix_config = FelixConfiguration.from_configuration_file(filepath=config_file, diff --git a/nucypher/config/node.py b/nucypher/config/node.py index 159e1def7..85424726f 100644 --- a/nucypher/config/node.py +++ b/nucypher/config/node.py @@ -37,6 +37,7 @@ from constant_sorrow.constants import ( from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve from cryptography.x509 import Certificate +from eth_utils import to_checksum_address, is_checksum_address from twisted.logger import Logger from umbral.signing import Signature @@ -286,7 +287,7 @@ class NodeConfiguration(ABC): def __write(self, password: str): if not self.federated_only: - self.connect_to_blockchain() + self.connect_to_blockchain() # Needed for access to ethereum node addresses and NC key signing _new_installation_path = self.initialize(password=password, download_registry=self.download_registry) _configuration_filepath = self.to_configuration_file(filepath=self.config_file_location) @@ -634,11 +635,18 @@ class NodeConfiguration(ABC): if not os.path.exists(data_dir): os.mkdir(data_dir) - client_version = self.interface.w3.clientVersion + client_version = self.blockchain.interface.w3.clientVersion if 'Geth' in client_version: checksum_address = NuCypherGethDevnetProcess.ensure_account_exists(password=password, data_dir=data_dir) + else: + raise RuntimeError("THIS IS A TEMPORARY DEBUGGING EXCEPTION") # TODO + + # Addresses read from some node keyrings are *not* returned in checksum format. + checksum_address = to_checksum_address(checksum_address) + assert is_checksum_address(checksum_address), f"INVALID ETH ADDRESS {checksum_address}" + # Use explicit address elif self.checksum_public_address: checksum_address = self.checksum_public_address