Attaching the integrated provider process to config and blockchain classes

pull/1040/head
Kieran Prasch 2019-04-28 06:08:00 +03:00
parent 600decc8b4
commit ad84a526bb
No known key found for this signature in database
GPG Key ID: 199AB839D4125A62
5 changed files with 93 additions and 52 deletions

View File

@ -89,7 +89,7 @@ class NucypherTokenActor:
self.checksum_public_address = checksum_address # type: str
if blockchain is None:
blockchain = Blockchain.connect()
blockchain = Blockchain.connect() # Attempt to connect
self.blockchain = blockchain
self.token_agent = NucypherTokenAgent()

View File

@ -39,9 +39,12 @@ class Blockchain:
class ConnectionNotEstablished(RuntimeError):
pass
def __init__(self, interface: Union[BlockchainInterface, BlockchainDeployerInterface] = None):
def __init__(self,
provider_process=None,
interface: Union[BlockchainInterface, BlockchainDeployerInterface] = None):
self.log = Logger("blockchain")
self.__provider_process = provider_process
# Default interface
if interface is None:
@ -56,8 +59,8 @@ class Blockchain:
def __repr__(self):
class_name = self.__class__.__name__
r = "{}(interface={})"
return r.format(class_name, self.__interface)
r = "{}(interface={}, process={})"
return r.format(class_name, self.__interface, self.__provider_process)
@property
def interface(self) -> Union[BlockchainInterface, BlockchainDeployerInterface]:
@ -65,6 +68,7 @@ class Blockchain:
@classmethod
def connect(cls,
provider_process=None,
provider_uri: str = None,
registry: EthereumContractRegistry = None,
deployer: bool = False,
@ -74,6 +78,8 @@ class Blockchain:
fetch_registry: bool = True
) -> 'Blockchain':
log = Logger('blockchain-init')
if cls._instance is NO_BLOCKCHAIN_AVAILABLE:
if not registry and fetch_registry:
from nucypher.config.node import NodeConfiguration
@ -85,15 +91,23 @@ class Blockchain:
else:
registry = registry or EthereumContractRegistry()
# Spawn child process
if provider_process:
provider_process.start()
else:
log.info(f"Using external Web3 Provider '{provider_uri}'")
compiler = SolidityCompiler() if compile is True else None
InterfaceClass = BlockchainDeployerInterface if deployer is True else BlockchainInterface
interface = InterfaceClass(provider_uri=provider_uri, registry=registry, compiler=compiler)
if poa is True:
log.debug('Injecting POA middleware at layer 0')
interface.w3.middleware_onion.inject(geth_poa_middleware, layer=0)
cls._instance = cls(interface=interface)
cls._instance = cls(interface=interface, provider_process=provider_process)
else:
if provider_uri is not None:
existing_uri = cls._instance.interface.provider_uri
if (existing_uri != provider_uri) and not force:
@ -107,6 +121,11 @@ class Blockchain:
return cls._instance
@classmethod
def disconnect(cls):
if cls._instance.__provider_process:
cls._instance.__provider_process.stop()
def get_contract(self, name: str) -> Contract:
"""
Gets an existing contract from the registry, or raises UnknownContract

View File

@ -9,6 +9,7 @@ from nucypher.cli.config import nucypher_click_config
from nucypher.cli.types import NETWORK_PORT, EXISTING_READABLE_FILE, EIP55_CHECKSUM_ADDRESS
from nucypher.config.characters import FelixConfiguration
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
@click.command()
@ -54,14 +55,12 @@ def felix(click_config,
force):
click.clear()
if not click_config.quiet:
click.secho(FELIX_BANNER.format(checksum_address or ''))
ETH_NODE = None # TODO: Make constant
ETH_NODE = NO_BLOCKCHAIN_CONNECTION.bool_value(False)
if geth:
ETH_NODE = NuCypherGethDevnetProcess(config_root=config_root)
ETH_NODE.start()
provider_uri = ETH_NODE.provider_uri
if action == "init":
@ -76,17 +75,26 @@ def felix(click_config,
# Acquire Keyring Password
new_password = click_config.get_password(confirm=True)
new_felix_config = FelixConfiguration.generate(password=new_password,
config_root=config_root,
rest_host=host,
rest_port=discovery_port,
db_filepath=db_filepath,
domains={network} if network else None,
checksum_public_address=checksum_address,
download_registry=not no_registry,
registry_filepath=registry_filepath,
provider_uri=provider_uri,
poa=poa)
try:
new_felix_config = FelixConfiguration.generate(password=new_password,
config_root=config_root,
rest_host=host,
rest_port=discovery_port,
db_filepath=db_filepath,
domains={network} if network else None,
checksum_public_address=checksum_address,
download_registry=not no_registry,
registry_filepath=registry_filepath,
provider_uri=provider_uri,
provider_process=ETH_NODE,
poa=poa)
except Exception as e:
if click_config.debug:
raise
else:
click.secho(str(e), fg='red', bold=True)
raise click.Abort
# Paint Help
painting.paint_new_installation_help(new_configuration=new_felix_config,
@ -103,6 +111,7 @@ def felix(click_config,
felix_config = FelixConfiguration.from_configuration_file(filepath=config_file,
domains=domains,
registry_filepath=registry_filepath,
provider_process=ETH_NODE,
provider_uri=provider_uri,
rest_host=host,
rest_port=port,
@ -165,14 +174,15 @@ ETH ........ {str(eth_balance)}
elif action == 'run': # Start web services
FELIX.start(host=host,
port=port,
web_services=not dry_run,
distribution=True,
crash_on_error=click_config.debug)
try:
FELIX.blockchain.connect()
FELIX.start(host=host,
port=port,
web_services=not dry_run,
distribution=True,
crash_on_error=click_config.debug)
finally:
FELIX.blockchain.disconnect()
else:
raise click.BadArgumentUsage("No such argument {}".format(action))
if ETH_NODE:
ETH_NODE.stop()

View File

@ -148,7 +148,7 @@ class NodeConfiguration(ABC):
# Blockchain
poa: bool = False,
provider_uri: str = None,
geth: bool = False,
provider_process = None,
# Registry
registry_source: str = None,
@ -249,7 +249,7 @@ class NodeConfiguration(ABC):
#
self.poa = poa
self.provider_uri = provider_uri or self.DEFAULT_PROVIDER_URI
self.geth = geth
self.provider_process = provider_process or NO_BLOCKCHAIN_CONNECTION
self.blockchain = NO_BLOCKCHAIN_CONNECTION
self.accounts = NO_BLOCKCHAIN_CONNECTION
@ -286,8 +286,6 @@ class NodeConfiguration(ABC):
return node_config
def __write(self, password: str):
if not self.federated_only:
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)
@ -315,14 +313,16 @@ class NodeConfiguration(ABC):
"""
if self.federated_only:
raise NodeConfiguration.ConfigurationError("Cannot connect to blockchain in federated mode")
self.blockchain = Blockchain.connect(provider_uri=self.provider_uri,
compile=recompile_contracts,
poa=self.poa,
fetch_registry=True)
fetch_registry=True,
provider_process=self.provider_process)
# Read Ethereum Node Keyring
self.accounts = self.blockchain.interface.w3.eth.accounts
# Add Ethereum Peer
if enode:
if self.blockchain.interface.client_version == 'geth':
self.blockchain.interface.w3.geth.admin.addPeer(enode)
@ -376,7 +376,11 @@ class NodeConfiguration(ABC):
return payload
@classmethod
def from_configuration_file(cls, filepath: str = None, **overrides) -> 'NodeConfiguration':
def from_configuration_file(cls,
filepath: str = None,
provider_process=None,
**overrides) -> 'NodeConfiguration':
"""Initialize a NodeConfiguration from a JSON file."""
from nucypher.config.storages import NodeStorage
@ -414,7 +418,9 @@ class NodeConfiguration(ABC):
overrides = {k: v for k, v in overrides.items() if v is not None}
# Instantiate from merged params
node_configuration = cls(config_file_location=filepath, **{**payload, **overrides})
node_configuration = cls(config_file_location=filepath,
provider_process=provider_process,
**{**payload, **overrides})
return node_configuration
@ -569,6 +575,13 @@ class NodeConfiguration(ABC):
# Generate Installation Subdirectories
self._cache_runtime_filepaths()
#
# Blockchain
#
if not self.federated_only:
if self.provider_process:
self.provider_process.initialize_blockchain()
#
# Node Storage
#
@ -621,27 +634,26 @@ class NodeConfiguration(ABC):
*args, **kwargs)
def write_keyring(self, password: str, **generation_kwargs) -> NucypherKeyring:
# Get or create wallet address
# Note: It is assumed the blockchain is not yet available.
if not self.federated_only and not self.checksum_public_address:
# TODO: Bury deeper in keying and powers
try:
checksum_address = self.blockchain.interface.w3.eth.accounts[0] # etherbase
#
# Integrated Provider Process
#
except IndexError:
if self.provider_process:
data_dir = os.path.join(self.config_root, '.ethereum', NuCypherGethDevnetProcess._CHAIN_NAME)
if not os.path.exists(data_dir):
os.mkdir(data_dir)
if not os.path.exists(self.provider_process.data_dir):
os.mkdir(self.provider_process.data_dir)
client_version = self.blockchain.interface.w3.clientVersion
if 'Geth' in client_version:
checksum_address = NuCypherGethDevnetProcess.ensure_account_exists(password=password,
data_dir=data_dir)
# Get or create wallet address (geth etherbase)
checksum_address = self.provider_process.ensure_account_exists(password=password)
else:
raise RuntimeError("THIS IS A TEMPORARY DEBUGGING EXCEPTION") # TODO
else:
# Manual Web3 Provider, We assume is already running and available
self.connect_to_blockchain()
# TODO: Create etherbase over RPC instead
raise self.ConfigurationError(f'Web3 provider "{self.provider_uri}" does not have any accounts')
# Addresses read from some node keyrings are *not* returned in checksum format.
checksum_address = to_checksum_address(checksum_address)

View File

@ -19,7 +19,7 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import os
from typing import List, Tuple, Dict
from constant_sorrow.constants import NO_BLOCKCHAIN_AVAILABLE
from constant_sorrow.constants import NO_BLOCKCHAIN_AVAILABLE, TEST_PROVIDER_ON_MAIN_PROCESS
from twisted.logger import Logger
from web3 import Web3
from web3.middleware import geth_poa_middleware
@ -86,7 +86,7 @@ class TesterBlockchain(Blockchain):
if test_accounts is None:
test_accounts = self._default_test_accounts
super().__init__(*args, **kwargs)
super().__init__(provider_process=TEST_PROVIDER_ON_MAIN_PROCESS, *args, **kwargs)
self.log = Logger("test-blockchain")
self.attach_middleware(w3=self.interface.w3, poa=poa, free_transactions=free_transactions)