Nucypher and BlockchainConfig and it's tenticles; Related NucypherConstants and ContractInterface updates.

pull/276/head
Kieran Prasch 2018-05-21 12:37:22 -07:00
parent ea6ceba6c4
commit 722cf9918f
10 changed files with 169 additions and 95 deletions

View File

@ -118,12 +118,12 @@ class ContractDeployer:
raise self.ContractDeploymentError('{} deployer is already armed.'.format(self._contract_name))
# If the blockchain network is public, prompt the user
if self.blockchain._network not in self.blockchain.test_chains:
if self.blockchain.config.network not in self.blockchain.test_chains:
message = """
Are you sure you want to deploy {contract} on the {network} network?
Type {word} to arm the deployer.
""".format(contract=self._contract_name, network=self.blockchain._network, word=self._arming_word)
""".format(contract=self._contract_name, network=self.blockchain.config.network, word=self._arming_word)
answer = input(message)
if answer == self._arming_word:

View File

@ -40,7 +40,7 @@ class Character(object):
def __init__(self, attach_server=True, crypto_power: CryptoPower=None,
crypto_power_ups=None, is_me=True, network_middleware=None,
config: "NucypherConfig"=None) -> None:
config: "NucypherConfig"=None, *args, **kwargs):
"""
:param attach_server: Whether to attach a Server when this Character is
born.
@ -62,7 +62,7 @@ class Character(object):
Character, but there are scenarios in which its imaginable to be
represented by zero Characters or by more than one Character.
"""
# self.config = config if config is not None else NucypherConfig.get_config()
self.config = config if config is not None else NucypherConfig.get() # default
self.known_nodes = {}
self.log = getLogger("characters")

View File

@ -1,8 +1,13 @@
import json
import os
import warnings
from pathlib import Path
import maya
from web3 import IPCProvider
from web3.middleware import geth_poa_middleware
from nucypher.blockchain.eth.chains import Blockchain, TesterBlockchain
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
_DEFAULT_CONFIGURATION_DIR = os.path.join(str(Path.home()), '.nucypher')
@ -11,32 +16,6 @@ class NucypherConfigurationError(RuntimeError):
pass
class StakeConfig:
# __minimum_stake_amount = 0 # TODO
# __minimum_stake_duration = 0
def __init__(self, amount: int, periods: int, start_datetime):
assert StakeConfig.validate_stake(amount, periods, start_datetime)
self.amount = amount
self.start = start_datetime
self.periods = periods
@classmethod
def validate_stake(cls, amount: int, periods: int, start_datetime) -> bool:
rules = (
# (amount > cls.__minimum_stake_amount, 'Staking aount must be at least {min_amount}'), # TODO
(start_datetime < maya.now(), 'Start date/time must not be in the past.'),
# (periods > cls.__minimum_stake_duration, 'Staking duration must be at least {}'.format(cls.__minimum_stake_duration))
)
for rule, failure_message in rules:
if rule is False:
raise NucypherConfigurationError(failure_message)
else:
return True
class PolicyConfig:
__default_m = 6 # TODO!!!
__default_n = 10
@ -64,34 +43,113 @@ class NetworkConfig:
return self.__db_path
class BlockchainConfig:
__default_providers = (IPCProvider(ipc_path=os.path.join('/tmp/geth.ipc')),
# user-managed geth over IPC assumed
)
__default_network = 'tester'
__default_timeout = 120 # seconds
__configured_providers = list() # tracks active providers
def __init__(self, network: str=None, timeout: int=None,
compiler=None, registrar=None, deploy=False,
geth=True, tester=False):
# Parse configuration
if len(self.__configured_providers) == 0:
warnings.warn("No blockchain provider backends are configured, using default.", RuntimeWarning)
self.__providers = BlockchainConfig.__default_providers
self._providers = self.__configured_providers
self.__network = network if network is not None else self.__default_network
self.__timeout = timeout if timeout is not None else self.__default_timeout
if deploy is False:
from nucypher.blockchain.eth.interfaces import ContractInterface
interface_class = ContractInterface
else:
from nucypher.blockchain.eth.interfaces import DeployerInterface
interface_class = DeployerInterface
interface = interface_class(blockchain_config=self, sol_compiler=compiler, registrar=registrar)
if geth is True:
w3 = interface.w3
w3.middleware_stack.inject(geth_poa_middleware, layer=0)
if tester is True:
blockchain_class = TesterBlockchain
else:
blockchain_class = Blockchain
# Initial connection to blockchain via provider
self.chain = blockchain_class(interface=interface)
@classmethod
def add_provider(cls, provider):
cls.__configured_providers.append(provider)
@property
def providers(self) -> list:
return self._providers
@property
def network(self):
return self.__network
@property
def timeout(self):
return self.__timeout
class NucypherConfig:
__instance = None
__default_configuration_root = _DEFAULT_CONFIGURATION_DIR
__default_json_config_filepath = os.path.join(__default_configuration_root, 'conf.json')
def __init__(self,
keyring,
keyring=None,
blockchain_config: BlockchainConfig=None,
network_config: NetworkConfig=None,
policy_config: PolicyConfig=None,
stake_config: StakeConfig=None,
configuration_root: str=None,
json_config_filepath: str=None):
# Check for custom paths
self.__configuration_root = configuration_root or self.__default_configuration_root
self.__json_config_filepath = json_config_filepath or self.__json_config_filepath
self.__json_config_filepath = json_config_filepath or self.__default_json_config_filepath
# Subconfigurations
self.keyring = keyring # Everyone
self.stake = stake_config # Ursula
self.policy = policy_config # Alice / Ursula
self.network = network_config # Ursula
if blockchain_config is None:
blockchain_config = BlockchainConfig()
# Sub-configurations
self.keyring = keyring # Everyone
self.blockchain = blockchain_config # Everyone
self.policy = policy_config # Alice / Ursula
self.network = network_config # Ursula
if self.__instance is not None:
raise RuntimeError('Configuration not started')
else:
self.__instance = self
@classmethod
def from_json_config(cls, path: str=None):
def get(cls):
return cls.__instance
@classmethod
def reset(cls) -> None:
cls.__instance = None
def __read(cls, path: str=None):
"""TODO: Reads the config file and creates a NuCypherConfig instance"""
with open(cls.__default_json_config_filepath, 'r') as config_file:
data = json.loads(config_file.read())
def to_json_config(self, path: str=None):
def __write(self, path: str=None):
"""TODO: Serializes a configuration and saves it to the local filesystem."""
path = path or self.__default_json_config_filepath

View File

@ -9,14 +9,14 @@ def test_dispatcher(web3, chain):
account = web3.eth.accounts[1]
# Load contract interface
contract_interface = chain.provider.get_contract_factory('ContractInterface')
contract_interface = chain.interface.get_contract_factory('ContractInterface')
# Deploy contracts and dispatcher for them
contract1_lib, _ = chain.provider.deploy_contract('ContractV1', 1)
contract2_lib, _ = chain.provider.deploy_contract('ContractV2', 1)
contract3_lib, _ = chain.provider.deploy_contract('ContractV3', 2)
contract2_bad_lib, _ = chain.provider.deploy_contract('ContractV2Bad')
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract1_lib.address)
contract1_lib, _ = chain.interface.deploy_contract('ContractV1', 1)
contract2_lib, _ = chain.interface.deploy_contract('ContractV2', 1)
contract3_lib, _ = chain.interface.deploy_contract('ContractV3', 2)
contract2_bad_lib, _ = chain.interface.deploy_contract('ContractV2Bad')
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract1_lib.address)
upgrades = dispatcher.events.Upgraded.createFilter(fromBlock=0)
assert dispatcher.functions.target().call() == contract1_lib.address

View File

@ -8,11 +8,11 @@ from eth_tester.backends.pyevm.main import get_default_genesis_params, get_defau
from web3 import Web3
from nucypher.blockchain.eth.agents import MinerAgent, NucypherTokenAgent
from nucypher.blockchain.eth.constants import NucypherMinerConfig
from nucypher.blockchain.eth.constants import NucypherMinerConstants
from nucypher.blockchain.eth.deployers import MinerEscrowDeployer, NucypherTokenDeployer
class MockNucypherMinerConfig(NucypherMinerConfig):
class MockNucypherMinerConstants(NucypherMinerConstants):
"""Speed things up a bit"""
# _hours_per_period = 24 # Hours
# min_locked_periods = 1 # Minimum locked periods
@ -38,7 +38,7 @@ class MockTokenAgent(NucypherTokenAgent):
return receipts
class MockMinerAgent(MinerAgent, MockNucypherMinerConfig):
class MockMinerAgent(MinerAgent, MockNucypherMinerConstants):
"""MinerAgent with faked config subclass"""
def spawn_random_miners(self, addresses: list) -> list:
@ -71,7 +71,7 @@ class MockNucypherTokenDeployer(NucypherTokenDeployer):
agency = MockTokenAgent
class MockMinerEscrowDeployer(MinerEscrowDeployer, MockNucypherMinerConfig):
class MockMinerEscrowDeployer(MinerEscrowDeployer, MockNucypherMinerConstants):
"""Helper class for MockMinerAgent, using a mock miner config"""
agency = MockMinerAgent

View File

@ -57,10 +57,10 @@ def test_anybody_can_verify():
"""
# Alice can sign by default, by dint of her _default_crypto_powerups.
alice = Alice()
alice = Alice(config=nucypher_test_config, policy_agent=mock_policy_agent)
# So, our story is fairly simple: an everyman meets Alice.
somebody = Character()
somebody = Character(config=nucypher_test_config)
# Alice signs a message.
message = b"A message for all my friends who can only verify and not sign."

View File

@ -2,8 +2,8 @@ from nucypher.characters import Ursula
from nucypher.crypto.powers import SigningPower
def test_ursula_generates_self_signed_cert():
ursula = Ursula(attach_server=False)
def test_ursula_generates_self_signed_cert(nucypher_test_config):
ursula = Ursula(attach_server=False, config=nucypher_test_config)
cert, cert_private_key = ursula.generate_self_signed_certificate()
public_key_numbers = ursula.public_key(SigningPower).to_cryptography_pubkey().public_numbers()
assert cert.public_key().public_numbers() == public_key_numbers

View File

@ -1,21 +1,20 @@
import os
import shutil
import signal
import subprocess
import tempfile
from os.path import abspath, dirname
import pytest
import shutil
import time
from eth_tester import EthereumTester, PyEVMBackend
from eth_tester import EthereumTester
from geth import LoggingMixin, DevGethProcess
from web3 import EthereumTesterProvider, IPCProvider, Web3
from web3.middleware import geth_poa_middleware
from os.path import abspath, dirname
from web3 import EthereumTesterProvider, IPCProvider
from nucypher.blockchain.eth.chains import TheBlockchain, TesterBlockchain
from nucypher.blockchain.eth.deployers import PolicyManagerDeployer
from nucypher.blockchain.eth.interfaces import Registrar, DeployerInterface
from nucypher.blockchain.eth.interfaces import Registrar
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.config.configs import BlockchainConfig
from tests.blockchain.eth import contracts, utilities
from tests.blockchain.eth.utilities import MockMinerEscrowDeployer, TesterPyEVMBackend, MockNucypherTokenDeployer
@ -98,12 +97,14 @@ def auto_geth_ipc_provider():
@pytest.fixture(scope='module')
def pyevm_provider():
"""
Provider backend
Test provider backend
https: // github.com / ethereum / eth - tester # available-backends
"""
overrides = {'gas_limit': 4626271}
pyevm_backend = TesterPyEVMBackend(genesis_overrides=overrides)
# pyevm_backend = PyEVMBackend()
eth_tester = EthereumTester(backend=pyevm_backend, auto_mine_transactions=True)
pyevm_provider = EthereumTesterProvider(ethereum_tester=eth_tester)
@ -123,46 +124,50 @@ def solidity_compiler():
@pytest.fixture(scope='module')
def web3(pyevm_provider):
def registrar():
_, filepath = tempfile.mkstemp()
test_registrar = Registrar(chain_name='tester', registrar_filepath=filepath)
yield test_registrar
os.remove(filepath)
w3 = Web3(providers=pyevm_provider)
w3.middleware_stack.inject(geth_poa_middleware, layer=0)
@pytest.fixture(scope='module')
def blockchain_config(pyevm_provider, solidity_compiler, registrar):
BlockchainConfig.add_provider(pyevm_provider)
config = BlockchainConfig(compiler=solidity_compiler, registrar=registrar, deploy=True, tester=True) # TODO: pass in address
yield config
config.chain.sever()
del config
@pytest.fixture(scope='module')
def deployer_interface(blockchain_config):
interface = blockchain_config.chain.interface
w3 = interface.w3
if len(w3.eth.accounts) == 1:
utilities.generate_accounts(w3=w3, quantity=9)
assert len(w3.eth.accounts) == 10
yield w3
yield interface
@pytest.fixture(scope='module')
def contract_provider(web3, registrar, solidity_compiler):
tester_provider = ContractProvider(provider_backend=web3, registrar=registrar, sol_compiler=solidity_compiler)
yield tester_provider
def web3(deployer_interface):
"""Compadibility fixture"""
return deployer_interface.w3
@pytest.fixture(scope='module')
def registrar():
_, filepath = tempfile.mkstemp()
registrar = Registrar(chain_name='tester', registrar_filepath=filepath)
yield registrar
os.remove(filepath)
def chain(deployer_interface, airdrop_ether=False):
chain = deployer_interface.blockchain_config.chain
@pytest.fixture(scope='module')
def chain(contract_provider, airdrop=False):
chain = TesterBlockchain(contract_provider=contract_provider)
if airdrop:
if airdrop_ether:
one_million_ether = 10 ** 6 * 10 ** 18 # wei -> ether
chain._global_airdrop(amount=one_million_ether)
chain.ether_airdrop(amount=one_million_ether)
yield chain
del chain
TheBlockchain._TheBlockchain__instance = None
#
# Deployers #
#

View File

@ -7,10 +7,9 @@ import pytest
from constant_sorrow import constants
from sqlalchemy.engine import create_engine
from nucypher.blockchain.eth.chains import Blockchain
from nucypher.characters import Alice, Bob
from nucypher.keystore import keystore
from nucypher.keystore.db import Base
from nucypher.config.configs import NucypherConfig
from nucypher.crypto.signature import SignatureStamp
from nucypher.data_sources import DataSource
from nucypher.keystore import keystore
@ -21,6 +20,17 @@ from tests.utilities import NUMBER_OF_URSULAS_IN_NETWORK, MockNetworkyStuff, mak
URSULA_PORT, EVENT_LOOP
@pytest.fixture(scope="module")
def nucypher_test_config(blockchain_config):
config = NucypherConfig(keyring="this is the most secure password in the world.",
blockchain_config=blockchain_config)
yield config
NucypherConfig.reset()
Blockchain.sever()
del config
@pytest.fixture(scope="module")
def idle_policy(alice, bob):
"""
@ -53,8 +63,8 @@ def enacted_policy(idle_policy, ursulas):
@pytest.fixture(scope="module")
def alice(ursulas):
ALICE = Alice(network_middleware=MockNetworkyStuff(ursulas))
def alice(ursulas, mock_policy_agent, nucypher_test_config):
ALICE = Alice(network_middleware=MockNetworkyStuff(ursulas), policy_agent=mock_policy_agent, config=nucypher_test_config)
ALICE.server.listen(8471)
ALICE.__resource_id = b"some_resource_id"
EVENT_LOOP.run_until_complete(ALICE.server.bootstrap([("127.0.0.1", u.dht_port) for u in ursulas]))
@ -69,8 +79,8 @@ def bob(ursulas):
@pytest.fixture(scope="module")
def ursulas():
URSULAS = make_ursulas(NUMBER_OF_URSULAS_IN_NETWORK, URSULA_PORT)
def ursulas(nucypher_test_config):
URSULAS = make_ursulas(NUMBER_OF_URSULAS_IN_NETWORK, URSULA_PORT, config=nucypher_test_config)
yield URSULAS
# Remove the DBs that have been sprayed hither and yon.
for _u in range(NUMBER_OF_URSULAS_IN_NETWORK):

View File

@ -3,6 +3,7 @@ import asyncio
from apistar.test import TestClient
from nucypher.characters import Ursula
from nucypher.config.configs import NucypherConfig
from nucypher.network.node import NetworkyStuff
from nucypher.policy.models import ArrangementResponse
@ -15,7 +16,7 @@ URSULA_PORT = 7468
NUMBER_OF_URSULAS_IN_NETWORK = 6
def make_ursulas(how_many_ursulas: int, ursula_starting_port: int) -> list:
def make_ursulas(how_many_ursulas: int, ursula_starting_port: int, config: NucypherConfig) -> list:
"""
:param how_many_ursulas: How many Ursulas to create.
:param ursula_starting_port: The port of the first created Ursula; subsequent Ursulas will increment the port number by 1.
@ -26,7 +27,7 @@ def make_ursulas(how_many_ursulas: int, ursula_starting_port: int) -> list:
URSULAS = []
for _u in range(how_many_ursulas):
port = ursula_starting_port + _u
_URSULA = Ursula(dht_port=port, ip_address="127.0.0.1", db_name="test-{}".format(port), rest_port=port+100) # TODO: Make ports unstupid and more clear.
_URSULA = Ursula(dht_port=port, ip_address="127.0.0.1", db_name="test-{}".format(port), rest_port=port+100, config=config) # TODO: Make ports unstupid and more clear.
class MockDatastoreThreadPool(object):
def callInThread(self, f, *args, **kwargs):