mirror of https://github.com/nucypher/nucypher.git
Support dynamicly added, lazy, multiple blockchain providers
parent
674410d1f1
commit
b5e029a4ca
|
@ -1,6 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from typing import Tuple
|
from typing import Tuple, Union
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from constant_sorrow import constants
|
from constant_sorrow import constants
|
||||||
|
@ -8,6 +8,7 @@ from eth_keys.datatypes import PublicKey, Signature
|
||||||
from eth_utils import to_canonical_address
|
from eth_utils import to_canonical_address
|
||||||
from web3 import Web3, WebsocketProvider, HTTPProvider, IPCProvider
|
from web3 import Web3, WebsocketProvider, HTTPProvider, IPCProvider
|
||||||
from web3.contract import Contract
|
from web3.contract import Contract
|
||||||
|
from web3.providers import BaseProvider
|
||||||
from web3.providers.eth_tester.main import EthereumTesterProvider
|
from web3.providers.eth_tester.main import EthereumTesterProvider
|
||||||
|
|
||||||
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
|
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
|
||||||
|
@ -215,15 +216,22 @@ class ControlCircumflex:
|
||||||
#
|
#
|
||||||
# Providers
|
# Providers
|
||||||
#
|
#
|
||||||
|
|
||||||
|
self.w3 = constants.NO_BLOCKCHAIN_CONNECTION
|
||||||
|
self.__providers = providers if providers is not None else constants.NO_BLOCKCHAIN_CONNECTION
|
||||||
|
|
||||||
if provider_uri and providers:
|
if provider_uri and providers:
|
||||||
raise self.InterfaceError("Pass a provider URI string, or a list of provider instances.")
|
raise self.InterfaceError("Pass a provider URI string, or a list of provider instances.")
|
||||||
|
elif provider_uri:
|
||||||
self.provider_uri = provider_uri
|
self.provider_uri = provider_uri
|
||||||
self._providers = list() if providers is None else providers
|
self.add_provider(provider_uri=provider_uri)
|
||||||
|
elif providers:
|
||||||
# If custom __providers are not injected...
|
self.provider_uri = constants.MANUAL_PROVIDERS_SET
|
||||||
self.w3 = constants.NO_BLOCKCHAIN_CONNECTION
|
for provider in providers:
|
||||||
if autoconnect is True:
|
self.add_provider(provider)
|
||||||
self.connect(provider_uri=self.provider_uri)
|
else:
|
||||||
|
# TODO: Emit a warning / log: No provider supplied for blockchain interface
|
||||||
|
pass
|
||||||
|
|
||||||
# if a SolidityCompiler class instance was passed, compile from solidity source code
|
# if a SolidityCompiler class instance was passed, compile from solidity source code
|
||||||
recompile = True if compiler is not None else False
|
recompile = True if compiler is not None else False
|
||||||
|
@ -231,7 +239,7 @@ class ControlCircumflex:
|
||||||
self.__sol_compiler = compiler
|
self.__sol_compiler = compiler
|
||||||
|
|
||||||
# Setup the registry and base contract factory cache
|
# Setup the registry and base contract factory cache
|
||||||
registry = registry if registry is not None else EthereumContractRegistry()
|
registry = registry if registry is not None else EthereumContractRegistry().from_config()
|
||||||
self._registry = registry
|
self._registry = registry
|
||||||
|
|
||||||
if self.__recompile is True:
|
if self.__recompile is True:
|
||||||
|
@ -239,64 +247,38 @@ class ControlCircumflex:
|
||||||
interfaces = self.__sol_compiler.compile()
|
interfaces = self.__sol_compiler.compile()
|
||||||
self.__raw_contract_cache = interfaces
|
self.__raw_contract_cache = interfaces
|
||||||
|
|
||||||
def connect(self,
|
# Auto-connect
|
||||||
provider_uri: str = None,
|
self.autoconnect = autoconnect
|
||||||
providers: list = None):
|
if self.autoconnect is True:
|
||||||
|
self.connect()
|
||||||
|
|
||||||
if provider_uri is None and not providers:
|
def connect(self):
|
||||||
raise self.InterfaceError("No URI supplied.")
|
|
||||||
|
|
||||||
if provider_uri and not providers:
|
if self.__providers is constants.NO_BLOCKCHAIN_CONNECTION:
|
||||||
uri_breakdown = urlparse(provider_uri)
|
raise self.InterfaceError("There are no configured blockchain providers")
|
||||||
elif providers and not provider_uri:
|
|
||||||
raise NotImplementedError
|
|
||||||
else:
|
|
||||||
raise self.InterfaceError("Pass a provider URI string or a list of providers, not both.")
|
|
||||||
|
|
||||||
# stub
|
|
||||||
if providers is None:
|
|
||||||
providers = list()
|
|
||||||
|
|
||||||
# IPC
|
|
||||||
if uri_breakdown.scheme == 'ipc':
|
|
||||||
provider = IPCProvider(ipc_path=uri_breakdown.path, timeout=self.timeout)
|
|
||||||
|
|
||||||
# Websocket
|
|
||||||
elif uri_breakdown.scheme == 'ws':
|
|
||||||
provider = WebsocketProvider(endpoint_uri=provider_uri)
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
# HTTP
|
|
||||||
elif uri_breakdown.scheme in ('http', 'https'):
|
|
||||||
provider = HTTPProvider(endpoint_uri=provider_uri)
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise self.InterfaceError("'{}' is not a blockchain provider protocol".format(uri_breakdown.scheme))
|
|
||||||
|
|
||||||
providers.append(provider)
|
|
||||||
|
|
||||||
# Connect
|
# Connect
|
||||||
self._providers = providers
|
web3_instance = Web3(providers=self.__providers) # Instantiate Web3 object with provider
|
||||||
web3_instance = Web3(providers=self._providers) # Instantiate Web3 object with provider
|
|
||||||
self.w3 = web3_instance
|
self.w3 = web3_instance
|
||||||
|
|
||||||
# Check connection
|
# Check connection
|
||||||
if not self.is_connected:
|
if not self.is_connected:
|
||||||
raise self.InterfaceError('Failed to connect to {}'.format(provider_uri))
|
raise self.InterfaceError('Failed to connect to providers: {}'.format(self.__providers))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_config(cls, filepath=None, registry_filepath: str=None) -> 'ControlCircumflex':
|
def from_config(cls, filepath=None) -> 'ControlCircumflex':
|
||||||
|
# Parse
|
||||||
filepath = filepath if filepath is None else DEFAULT_INI_FILEPATH
|
filepath = filepath if filepath is None else DEFAULT_INI_FILEPATH
|
||||||
payload = parse_blockchain_config(filepath=filepath)
|
payload = parse_blockchain_config(filepath=filepath)
|
||||||
|
|
||||||
|
# Init deps
|
||||||
compiler = SolidityCompiler() if payload['compile'] else None
|
compiler = SolidityCompiler() if payload['compile'] else None
|
||||||
|
|
||||||
registry = EthereumContractRegistry.from_config(filepath=filepath)
|
registry = EthereumContractRegistry.from_config(filepath=filepath)
|
||||||
|
|
||||||
interface_class = ControlCircumflex if not payload['deploy'] else DeployerCircumflex
|
interface_class = ControlCircumflex if not payload['deploy'] else DeployerCircumflex
|
||||||
|
|
||||||
|
# init class
|
||||||
circumflex = interface_class(timeout=payload['timeout'],
|
circumflex = interface_class(timeout=payload['timeout'],
|
||||||
provider_uri=payload['provider_uri'],
|
provider_uri=payload['provider_uri'],
|
||||||
compiler=compiler,
|
compiler=compiler,
|
||||||
|
@ -320,35 +302,36 @@ class ControlCircumflex:
|
||||||
"""Return node version information"""
|
"""Return node version information"""
|
||||||
return self.w3.version.node # type of connected node
|
return self.w3.version.node # type of connected node
|
||||||
|
|
||||||
def add_provider(self, provider=None, endpoint_uri: str=None,
|
def add_provider(self,
|
||||||
websocket=False, ipc_path=None, timeout=None) -> None:
|
provider: Union[IPCProvider, WebsocketProvider, HTTPProvider] = None,
|
||||||
|
provider_uri: str = None,
|
||||||
|
timeout: int = None) -> None:
|
||||||
|
|
||||||
if provider is None:
|
if not provider_uri and not provider:
|
||||||
|
raise self.InterfaceError("No URI or provider instances supplied.")
|
||||||
|
|
||||||
# Validate parameters
|
if provider_uri and not provider:
|
||||||
if websocket and not endpoint_uri:
|
uri_breakdown = urlparse(provider_uri)
|
||||||
if ipc_path is not None:
|
|
||||||
raise self.InterfaceError("Use either HTTP/Websocket or IPC params, not both.")
|
|
||||||
raise self.InterfaceError('Must pass endpoint_uri when using websocket __providers.')
|
|
||||||
|
|
||||||
if ipc_path is not None:
|
# IPC
|
||||||
if endpoint_uri or websocket:
|
if uri_breakdown.scheme == 'ipc':
|
||||||
raise self.InterfaceError("Use either HTTP/Websocket or IPC params, not both.")
|
provider = IPCProvider(ipc_path=uri_breakdown.path, timeout=timeout)
|
||||||
|
|
||||||
|
# Websocket
|
||||||
|
elif uri_breakdown.scheme == 'ws':
|
||||||
|
provider = WebsocketProvider(endpoint_uri=provider_uri)
|
||||||
|
|
||||||
|
# HTTP
|
||||||
|
elif uri_breakdown.scheme in ('http', 'https'):
|
||||||
|
provider = HTTPProvider(endpoint_uri=provider_uri)
|
||||||
|
|
||||||
# HTTP / Websocket Provider
|
|
||||||
if endpoint_uri is not None:
|
|
||||||
if websocket is True:
|
|
||||||
provider = WebsocketProvider(endpoint_uri)
|
|
||||||
else:
|
else:
|
||||||
provider = HTTPProvider(endpoint_uri)
|
raise self.InterfaceError("'{}' is not a blockchain provider protocol".format(uri_breakdown.scheme))
|
||||||
|
|
||||||
# IPC Provider
|
# lazy
|
||||||
elif ipc_path:
|
if self.__providers is constants.NO_BLOCKCHAIN_CONNECTION:
|
||||||
provider = IPCProvider(ipc_path=ipc_path, testnet=False, timeout=timeout)
|
self.__providers = list()
|
||||||
else:
|
self.__providers.append(provider)
|
||||||
raise self.InterfaceError("Invalid interface parameters. Pass endpoint_uri or ipc_path")
|
|
||||||
|
|
||||||
self._providers.append(provider)
|
|
||||||
|
|
||||||
def get_contract_factory(self, contract_name) -> Contract:
|
def get_contract_factory(self, contract_name) -> Contract:
|
||||||
"""Retrieve compiled interface data from the cache and return web3 contract"""
|
"""Retrieve compiled interface data from the cache and return web3 contract"""
|
||||||
|
|
|
@ -29,7 +29,7 @@ from nucypher.utilities.network import MockRestMiddleware
|
||||||
#
|
#
|
||||||
# Setup
|
# Setup
|
||||||
#
|
#
|
||||||
|
from nucypher_cli.main import BASE_DIR
|
||||||
|
|
||||||
test_contract_dir = os.path.join(BASE_DIR, 'tests', 'blockchain', 'eth', 'contracts', 'contracts')
|
test_contract_dir = os.path.join(BASE_DIR, 'tests', 'blockchain', 'eth', 'contracts', 'contracts')
|
||||||
constants.TEST_CONTRACTS_DIR(test_contract_dir)
|
constants.TEST_CONTRACTS_DIR(test_contract_dir)
|
||||||
|
|
Loading…
Reference in New Issue