Merge pull request #276 from KPrasch/actor-integration

Character + TokenActor integration and configuration
pull/284/head
Justin Holmes 2018-06-01 17:50:44 -04:00 committed by GitHub
commit a97f720b24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 581 additions and 465 deletions

View File

@ -3,6 +3,7 @@ cache: pip
python:
- '3.5'
- '3.6'
- '3.7-dev'
install:
- pip install pipenv --upgrade
- pipenv install --dev --three --skip-lock
@ -11,4 +12,7 @@ before_script:
- chmod +x "${VIRTUAL_ENV}/bin/solc"
script:
- pipenv run -- pytest --runslow --cov=nucypher -v tests
- codecov
- codecov
matrix:
allow_failures:
- python: '3.7-dev'

View File

@ -1,17 +1,19 @@
from abc import ABC
from collections import OrderedDict
from datetime import datetime
from typing import Tuple, List, Union
from nucypher.blockchain.eth.agents import NucypherTokenAgent
from nucypher.blockchain.eth.agents import NucypherTokenAgent, MinerAgent, PolicyAgent
class TokenActor(ABC):
class NucypherTokenActor:
class ActorError(Exception):
pass
def __init__(self, token_agent: NucypherTokenAgent, address: Union[bytes, str]):
def __init__(self, address: Union[str, bytes], token_agent: NucypherTokenAgent=None, *args, **kwargs):
if token_agent is None:
token_agent = NucypherTokenAgent()
self.token_agent = token_agent
if isinstance(address, bytes):
@ -28,32 +30,27 @@ class TokenActor(ABC):
def eth_balance(self):
"""Return this actors's current ETH balance"""
balance = self.token_agent.blockchain._chain.web3.eth.getBalance(self.address)
balance = self.token_agent.blockchain.interface.w3.eth.getBalance(self.address)
return balance
def token_balance(self):
"""Return this actors's current token balance"""
balance = self.token_agent.get_balance(address=self.address)
return balance
class Miner(TokenActor):
class Miner(NucypherTokenActor):
"""
Ursula - practically carrying a pickaxe.
Accepts a running blockchain, deployed token contract, and deployed escrow contract.
If the provided token and escrow contracts are not deployed,
ContractDeploymentError will be raised.
"""
class StakingError(TokenActor.ActorError):
class StakingError(NucypherTokenActor.ActorError):
pass
def __init__(self, miner_agent, address):
super().__init__(token_agent=miner_agent.token_agent, address=address)
def __init__(self, miner_agent: MinerAgent=None, *args, **kwargs):
if miner_agent is None:
miner_agent = MinerAgent(token_agent=NucypherTokenAgent())
super().__init__(token_agent=miner_agent.token_agent, *args, **kwargs)
self.miner_agent = miner_agent
miner_agent.miners.append(self) # Track Miners
@ -186,7 +183,7 @@ class Miner(TokenActor):
if entire_balance is True:
amount = self.miner_agent.contract.functions.getMinerInfo(self.miner_agent.MinerInfo.VALUE.value,
self.address, 0).call()
amount = self.blockchain.provider.w3.toInt(amount)
amount = self.blockchain.interface.w3.toInt(amount)
assert self.__validate_stake(amount=amount, lock_periods=lock_periods)
@ -210,7 +207,7 @@ class Miner(TokenActor):
count_bytes = self.miner_agent.contract.functions.getMinerIdsLength(self.address).call()
count = self.blockchain.provider.w3.toInt(count_bytes)
count = self.blockchain.interface.w3.toInt(count_bytes)
miner_ids = list()
for index in range(count):
@ -219,12 +216,19 @@ class Miner(TokenActor):
return tuple(miner_ids)
class PolicyAuthor(TokenActor):
class PolicyAuthor(NucypherTokenActor):
"""Alice"""
def __init__(self, address: bytes, policy_agent):
super().__init__(token_agent=policy_agent._token, address=address)
def __init__(self, policy_agent: PolicyAgent=None, *args, **kwargs):
if policy_agent is None:
# all defaults
token_agent = NucypherTokenAgent()
miner_agent = MinerAgent(token_agent=token_agent)
policy_agent = PolicyAgent(miner_agent=miner_agent)
self.policy_agent = policy_agent
super().__init__(token_agent=self.policy_agent.token_agent, *args, **kwargs)
self._arrangements = OrderedDict() # Track authored policies by id

View File

@ -1,13 +1,14 @@
import random
from abc import ABC
from enum import Enum
from functools import partial
from typing import Set, Generator, List
from nucypher.blockchain.eth import constants
from nucypher.blockchain.eth.constants import NucypherTokenConfig, NucypherMinerConfig
from web3.contract import Contract
from nucypher.blockchain.eth import constants
from nucypher.blockchain.eth.chains import Blockchain
from nucypher.blockchain.eth.constants import NucypherTokenConstants, NucypherMinerConstants
class EthereumContractAgent(ABC):
"""
@ -20,14 +21,19 @@ class EthereumContractAgent(ABC):
class ContractNotDeployed(Exception):
pass
def __init__(self, blockchain, contract: Contract=None, *args, **kwargs):
def __init__(self, blockchain: Blockchain=None, contract: Contract=None, *args, **kwargs):
if blockchain is None:
blockchain = Blockchain.connect()
self.blockchain = blockchain
if contract is None:
address = blockchain.provider.get_contract_address(contract_name=self._principal_contract_name)[-1] # TODO
contract = blockchain.provider.get_contract(address)
address = blockchain.interface.get_contract_address(contract_name=self._principal_contract_name)[-1] # TODO: Handle multiple
contract = blockchain.interface.get_contract(address)
self.__contract = contract
super().__init__(*args, **kwargs)
def __repr__(self):
class_name = self.__class__.__name__
r = "{}(blockchain={}, contract={})"
@ -50,7 +56,7 @@ class EthereumContractAgent(ABC):
@property
def origin(self) -> str:
return self.blockchain.provider.w3.eth.coinbase # TODO: make swappable
return self.blockchain.interface.w3.eth.coinbase # TODO: make swappable
def get_balance(self, address: str=None) -> int:
"""Get the balance of a token address, or of this contract address"""
@ -59,11 +65,11 @@ class EthereumContractAgent(ABC):
return self.contract.functions.balanceOf(address).call()
class NucypherTokenAgent(EthereumContractAgent, NucypherTokenConfig):
class NucypherTokenAgent(EthereumContractAgent, NucypherTokenConstants):
_principal_contract_name = "NuCypherToken"
class MinerAgent(EthereumContractAgent, NucypherMinerConfig):
class MinerAgent(EthereumContractAgent, NucypherMinerConstants):
"""
Wraps NuCypher's Escrow solidity smart contract
@ -106,7 +112,7 @@ class MinerAgent(EthereumContractAgent, NucypherMinerConfig):
count = self.contract.functions.getMinersLength().call()
for index in range(count):
addr = self.contract.functions.miners(index).call()
yield self.blockchain.provider.w3.toChecksumAddress(addr)
yield self.blockchain.interface.w3.toChecksumAddress(addr)
def sample(self, quantity: int=10, additional_ursulas: float=1.7, attempts: int=5, duration: int=10) -> List[str]:
"""
@ -157,6 +163,11 @@ class PolicyAgent(EthereumContractAgent):
_principal_contract_name = "PolicyManager"
def __init__(self, miner_agent: MinerAgent, *args, **kwargs):
super().__init__(blockchain=miner_agent.blockchain, *args, **kwargs)
self.miner_agent = miner_agent
self.token_agent = miner_agent.token_agent
def fetch_arrangement_data(self, arrangement_id: bytes) -> list:
blockchain_record = self.contract.functions.policies(arrangement_id).call()
return blockchain_record

View File

@ -1,88 +1,74 @@
import random
from abc import ABC
from typing import List
from typing import List, Union
from nucypher.blockchain.eth.constants import NucypherMinerConfig
from nucypher.blockchain.eth.interfaces import ContractProvider
from nucypher.blockchain.eth.constants import NucypherMinerConstants
from nucypher.blockchain.eth.interfaces import ContractInterface, DeployerInterface
class TheBlockchain(ABC):
"""
http://populus.readthedocs.io/en/latest/config.html#chains
class Blockchain:
"""A view of a blockchain through a provided interface"""
mainnet: Connects to the public ethereum mainnet via geth.
ropsten: Connects to the public ethereum ropsten testnet via geth.
tester: Uses an ephemeral in-memory chain backed by pyethereum.
testrpc: Uses an ephemeral in-memory chain backed by pyethereum.
temp: Local private chain whos data directory is removed when the chain is shutdown. Runs via geth.
"""
_network = NotImplemented
_default_timeout = 120
__instance = None
_instance = None
__default_interface_class = ContractInterface
test_chains = ('tester', )
transient_chains = test_chains + ('testrpc', 'temp')
public_chains = ('mainnet', 'ropsten')
class IsAlreadyRunning(RuntimeError):
pass
def __init__(self, interface: Union[ContractInterface, DeployerInterface]=None):
def __init__(self, contract_provider: ContractProvider):
if interface is None:
interface = self.__default_interface_class(blockchain_config=interface.config)
"""
Configures a populus project and connects to blockchain.network.
Transaction timeouts specified measured in seconds.
self.__interface = interface
self.config = interface.blockchain_config
http://populus.readthedocs.io/en/latest/chain.wait.html
"""
# Singleton #
if TheBlockchain.__instance is not None:
message = '{} is already running on {}. Use .get() to retrieve'.format(self.__class__.__name__,
self._network)
raise TheBlockchain.IsAlreadyRunning(message)
TheBlockchain.__instance = self
self.provider = contract_provider
@classmethod
def get(cls):
if cls.__instance is None:
class_name = cls.__name__
raise Exception('{} has not been created.'.format(class_name))
return cls.__instance
if self._instance is not None:
raise RuntimeError("Local chain already running")
else:
Blockchain._instance = self
def __repr__(self):
class_name = self.__class__.__name__
r = "{}(network={})"
return r.format(class_name, self._network)
r = "{}(interface={})"
return r.format(class_name, self.__interface)
@classmethod
def connect(cls):
if cls._instance is None:
raise RuntimeError('{} not running'.format(cls.__name__))
return cls._instance
@property
def interface(self):
return self.__interface
@classmethod
def sever(cls) -> None:
cls._instance = None
def get_contract(self, name):
"""
Gets an existing contract from the registrar,
or raises populus.contracts.exceptions.UnknownContract
Gets an existing contract from the registrar, or raises UnknownContract
if there is no contract data available for the name/identifier.
"""
return self.provider.get_contract(name)
return self.__interface.get_contract(name)
def wait_for_receipt(self, txhash, timeout=None) -> None:
timeout = timeout if timeout is not None else self._default_timeout
result = self.provider.w3.eth.waitForTransactionReceipt(txhash, timeout=timeout)
def wait_for_receipt(self, txhash, timeout=None) -> dict:
"""Wait for a receipt and return it"""
timeout = timeout if timeout is not None else self.config.timeout
result = self.__interface.w3.eth.waitForTransactionReceipt(txhash, timeout=timeout)
return result
class TesterBlockchain(TheBlockchain, NucypherMinerConfig):
"""Transient, in-memory, local, private chain"""
_network = 'tester'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class TesterBlockchain(Blockchain, NucypherMinerConstants):
"""
Blockchain subclass with additional test utility methods
and singleton-style instance caching to preserve the local blockchain state.
"""
def wait_for_receipt(self, txhash, timeout=None) -> None:
timeout = timeout if timeout is not None else self._default_timeout
result = self.provider.w3.eth.waitForTransactionReceipt(txhash, timeout=timeout)
timeout = timeout if timeout is not None else self.config.timeout
result = self.interface.w3.eth.waitForTransactionReceipt(txhash, timeout=timeout)
return result
def time_travel(self, hours: int=None, seconds: int=None, periods: int=None):
@ -107,27 +93,22 @@ class TesterBlockchain(TheBlockchain, NucypherMinerConfig):
else:
raise ValueError("Specify either hours, seconds, or lock_periods.")
end_timestamp = ((self.provider.w3.eth.getBlock(block_identifier='latest').timestamp + duration) // base) * base
self.provider.w3.eth.web3.testing.timeTravel(timestamp=end_timestamp)
self.provider.w3.eth.web3.testing.mine(1)
end_timestamp = ((self.interface.w3.eth.getBlock(block_identifier='latest').timestamp + duration) // base) * base
self.interface.w3.eth.web3.testing.timeTravel(timestamp=end_timestamp)
self.interface.w3.eth.web3.testing.mine(1)
def _global_airdrop(self, amount: int) -> List[str]:
"""Airdrops from creator address to all other addresses!"""
coinbase, *addresses = self.provider.w3.eth.accounts
def ether_airdrop(self, amount: int) -> List[str]:
"""Airdrops tokens from creator address to all other addresses!"""
coinbase, *addresses = self.__interface.w3.eth.accounts
tx_hashes = list()
for address in addresses:
tx = {'to': address, 'from': coinbase, 'value': amount}
txhash = self.provider.w3.eth.sendTransaction(tx)
_receipt = self.provider.w3.eth.waitForTransactionReceipt(txhash)
tx_hashes.append(txhash)
for txhash in tx_hashes:
self.wait_for_receipt(txhash)
return tx_hashes
#
# class TestRPCBlockchain:
#
# _network = 'testrpc'
# _default_timeout = 60
#
tx = {'to': address, 'from': coinbase, 'value': amount}
txhash = self.interface.w3.eth.sendTransaction(tx)
_receipt = self.wait_for_receipt(txhash)
tx_hashes.append(txhash)
return tx_hashes

View File

@ -1,15 +1,15 @@
NULL_ADDRESS = '0x' + '0' * 40
class NucypherTokenConfig:
class NucypherTokenConstants:
class TokenConfigError(ValueError):
pass
__subdigits = 18
_M = 10 ** __subdigits # Unit designation
__initial_supply = int(1e9) * _M # Initial token supply
__saturation = int(3.89e9) * _M # Token supply cap
M = 10 ** __subdigits # Unit designation
__initial_supply = int(1e9) * M # Initial token supply
__saturation = int(3.89e9) * M # Token supply cap
_remaining_supply = __saturation - __initial_supply # Remaining supply
@property
@ -17,7 +17,7 @@ class NucypherTokenConfig:
return self.__saturation
class NucypherMinerConfig:
class NucypherMinerConstants:
class MinerConfigError(ValueError):
pass
@ -26,10 +26,10 @@ class NucypherMinerConfig:
min_locked_periods = 30 # 720 Hours minimum
max_minting_periods = 365 # Maximum number of periods
min_allowed_locked = 15000 * NucypherTokenConfig._M
max_allowed_locked = int(4e6) * NucypherTokenConfig._M
min_allowed_locked = 15000 * NucypherTokenConstants.M
max_allowed_locked = int(4e6) * NucypherTokenConstants.M
__remaining_supply = NucypherTokenConfig._remaining_supply
__remaining_supply = NucypherTokenConstants._remaining_supply
__mining_coeff = [ # TODO
_hours_per_period,

View File

@ -3,12 +3,14 @@ from typing import Tuple, Dict
from web3.contract import Contract
from nucypher.blockchain.eth.agents import EthereumContractAgent, MinerAgent, NucypherTokenAgent, PolicyAgent
from nucypher.blockchain.eth.constants import NucypherTokenConfig, NucypherMinerConfig
from .chains import TheBlockchain
from nucypher.blockchain.eth.constants import NucypherTokenConstants, NucypherMinerConstants
from nucypher.blockchain.eth.interfaces import ContractInterface, DeployerInterface
from .chains import Blockchain
class ContractDeployer:
_interface_class = DeployerInterface
agency = NotImplemented
_contract_name = NotImplemented
_arming_word = "I UNDERSTAND"
@ -16,14 +18,14 @@ class ContractDeployer:
class ContractDeploymentError(Exception):
pass
def __init__(self, blockchain: TheBlockchain):
def __init__(self, blockchain: Blockchain):
self.__armed = False
self._contract = None
self.deployment_receipt = None
self.__dispatcher = NotImplemented
# Sanity check
if not isinstance(blockchain, TheBlockchain):
if not isinstance(blockchain, Blockchain):
error = 'Only TheBlockchain can be used to create a deployer, got {}.'
raise ValueError(error.format(type(blockchain)))
self.blockchain = blockchain
@ -38,6 +40,10 @@ class ContractDeployer:
else:
return address
@property
def contract(self):
return self._contract
@property
def dispatcher(self):
return self.__dispatcher
@ -65,9 +71,6 @@ class ContractDeployer:
rules = (
(self.is_armed is True, 'Contract not armed'),
(self.is_deployed is not True, 'Contract already deployed'),
# (self.blockchain.provider.are_contract_dependencies_available(self._contract_name),
# 'Blockchain contract dependencies unmet'),
)
disqualifications = list()
@ -90,19 +93,14 @@ class ContractDeployer:
message = '{} contract is not deployed. Arm, then deploy.'.format(class_name)
raise self.ContractDeploymentError(message)
# http: // populus.readthedocs.io / en / latest / chain.contracts.html # checking-availability-of-contracts
available = bool(self.blockchain.provider.are_contract_dependencies_available(self._contract_name))
if not available:
raise self.ContractDeploymentError('Contract is not available')
return True
def _wrap_government(self, dispatcher_contract: Contract, target_contract: Contract) -> Contract:
# Wrap the contract
wrapped_contract = self.blockchain.provider.w3.eth.contract(abi=target_contract.abi,
address=dispatcher_contract.address,
ContractFactoryClass=Contract)
wrapped_contract = self.blockchain.interface.w3.eth.contract(abi=target_contract.abi,
address=dispatcher_contract.address,
ContractFactoryClass=Contract)
return wrapped_contract
def arm(self, fail_on_abort=True) -> None:
@ -120,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:
@ -156,14 +154,18 @@ class ContractDeployer:
return agent
class NucypherTokenDeployer(ContractDeployer, NucypherTokenConfig):
class NucypherTokenDeployer(ContractDeployer, NucypherTokenConstants):
_contract_name = 'NuCypherToken'
agency = NucypherTokenAgent
def __init__(self, blockchain):
def __init__(self, blockchain, deployer_address=None):
if not type(blockchain.interface) is self._interface_class:
raise ValueError("{} must be used to create a {}".format(self._interface_class.__name__,
self.__class__.__name__))
super().__init__(blockchain=blockchain)
self._creator = self.blockchain.provider.w3.eth.accounts[0] # TODO: make swappable
self._creator = deployer_address
def deploy(self) -> str:
"""
@ -176,7 +178,7 @@ class NucypherTokenDeployer(ContractDeployer, NucypherTokenConfig):
is_ready, _disqualifications = self.check_ready_to_deploy(fail=True)
assert is_ready
_contract, deployment_txhash = self.blockchain.provider.deploy_contract(
_contract, deployment_txhash = self.blockchain.interface.deploy_contract(
self._contract_name,
self.saturation)
@ -199,14 +201,14 @@ class DispatcherDeployer(ContractDeployer):
def deploy(self) -> str:
dispatcher_contract, txhash = self.blockchain.provider.deploy_contract(
dispatcher_contract, txhash = self.blockchain.interface.deploy_contract(
'Dispatcher', self.target_contract.address)
self._contract = dispatcher_contract
return txhash
class MinerEscrowDeployer(ContractDeployer, NucypherMinerConfig):
class MinerEscrowDeployer(ContractDeployer, NucypherMinerConstants):
"""
Deploys the MinerEscrow ethereum contract to the blockchain. Depends on NucypherTokenAgent
"""
@ -244,9 +246,9 @@ class MinerEscrowDeployer(ContractDeployer, NucypherMinerConfig):
# 1 - Deploy #
the_escrow_contract, deploy_txhash, = \
self.blockchain.provider.deploy_contract(self._contract_name,
self.token_agent.contract_address,
*self.mining_coefficient)
self.blockchain.interface.deploy_contract(self._contract_name,
self.token_agent.contract_address,
*self.mining_coefficient)
# 2 - Deploy the dispatcher used for updating this contract #
dispatcher_deployer = DispatcherDeployer(token_agent=self.token_agent, target_contract=the_escrow_contract)
@ -254,7 +256,7 @@ class MinerEscrowDeployer(ContractDeployer, NucypherMinerConfig):
dispatcher_deploy_txhash = dispatcher_deployer.deploy()
# Cache the dispatcher contract
dispatcher_contract = dispatcher_deployer._contract
dispatcher_contract = dispatcher_deployer.contract
self.__dispatcher_contract = dispatcher_contract
# Wrap the escrow contract (Govern)
@ -296,9 +298,11 @@ class PolicyManagerDeployer(ContractDeployer):
agency = PolicyAgent
_contract_name = 'PolicyManager'
def make_agent(self) -> EthereumContractAgent:
agent = self.agency(miner_agent=self.miner_agent, contract=self._contract)
return agent
def __init__(self, miner_agent):
# self.token_deployer = miner_escrow_deployer.token_agent
# self.miner_escrow_deployer = miner_escrow_deployer
self.token_agent = miner_agent.token_agent
self.miner_agent = miner_agent
super().__init__(blockchain=self.miner_agent.blockchain)
@ -308,7 +312,7 @@ class PolicyManagerDeployer(ContractDeployer):
assert is_ready
# Creator deploys the policy manager
the_policy_manager_contract, deploy_txhash = self.blockchain.provider.deploy_contract(
the_policy_manager_contract, deploy_txhash = self.blockchain.interface.deploy_contract(
self._contract_name, self.miner_agent.contract_address)
dispatcher_deployer = DispatcherDeployer(token_agent=self.token_agent, target_contract=the_policy_manager_contract)
@ -316,7 +320,7 @@ class PolicyManagerDeployer(ContractDeployer):
dispatcher_deploy_txhash = dispatcher_deployer.deploy()
# Cache the dispatcher contract
dispatcher_contract = dispatcher_deployer._contract
dispatcher_contract = dispatcher_deployer.contract
self.__dispatcher_contract = dispatcher_contract
# Wrap the escrow contract (Govern)
@ -366,7 +370,7 @@ class UserEscrowDeployer(ContractDeployer):
self.policy_deployer.contract_address],
deploy_transaction = {'from': self.token_deployer.contract_address}
the_user_escrow_contract, deploy_txhash = self.blockchain.provider.deploy_contract(
the_user_escrow_contract, deploy_txhash = self.blockchain.interface.deploy_contract(
self._contract_name,
*deployment_args)

View File

@ -19,8 +19,8 @@ class Registrar:
WARNING: Unless you are developing NuCypher, you most
likely won't ever need to use this.
"""
__DEFAULT_REGISTRAR_FILEPATH = os.path.join(_DEFAULT_CONFIGURATION_DIR, 'registrar.json')
__DEFAULT_CHAIN_NAME = 'tester'
__default_registrar_path = os.path.join(_DEFAULT_CONFIGURATION_DIR, 'registrar.json')
__default_chain_name = 'tester'
class UnknownContract(KeyError):
pass
@ -29,8 +29,8 @@ class Registrar:
pass
def __init__(self, chain_name: str=None, registrar_filepath: str=None):
self._chain_name = chain_name or self.__DEFAULT_CHAIN_NAME
self.__registrar_filepath = registrar_filepath or self.__DEFAULT_REGISTRAR_FILEPATH
self._chain_name = chain_name or self.__default_chain_name
self.__registrar_filepath = registrar_filepath or self.__default_registrar_path
def __write(self, registrar_data: dict) -> None:
"""
@ -69,7 +69,7 @@ class Registrar:
the value is the Registrar object for that chain.
Optionally, accepts a registrar filepath.
"""
filepath = registrar_filepath or cls.__DEFAULT_REGISTRAR_FILEPATH
filepath = registrar_filepath or cls.__default_registrar_path
instance = cls(registrar_filepath=filepath)
registrar_data = instance.__read()
@ -135,7 +135,7 @@ class Registrar:
if len(contracts) > 0:
return contracts
else:
message = "Could not identify a contract name or address with {}".format(contract_name)
message = 'Contract name or address: "{}" for chain: "{}" was not found in the registrar'.format(contract_name, self._chain_name)
raise self.UnknownContract(message)
def dump_contract(self, address: str=None) -> dict:
@ -155,26 +155,25 @@ class Registrar:
if contract_data['name'] == address:
return contract_data
else:
raise self.UnknownContract('No known contract with address {}'.format(address))
raise self.UnknownContract('No known contract with address: {}'.format(address))
class ContractProvider:
class ContractInterface:
"""
Interacts with a solidity compiler and a registrar in order to instantiate compiled
ethereum contracts with the given web3 provider backend.
"""
def __init__(self, provider_backend: Web3,
registrar: Registrar,
deployer_address: str=None,
sol_compiler: SolidityCompiler=None):
class ContractInterfaceError(Exception):
pass
self.w3 = provider_backend
def __init__(self, blockchain_config=None, registrar: Registrar=None, sol_compiler: SolidityCompiler=None):
"""Contracts are re-compiled if an instance is passed"""
# TODO: Move to deployers?
if deployer_address is None:
deployer_address = self.w3.eth.coinbase # coinbase / etherbase
self.deployer_address = deployer_address
self.blockchain_config = blockchain_config
web3_instance = Web3(providers=self.blockchain_config.providers)
self.w3 = web3_instance
# if a SolidityCompiler class instance was passed, compile from sources
if sol_compiler is not None:
@ -184,24 +183,24 @@ class ContractProvider:
self.__recompile = recompile
self.__sol_compiler = sol_compiler
# Setup the registrar and base contract factory cache
if registrar is None:
registrar = Registrar(chain_name=self.blockchain_config.network)
self._registrar = registrar
if self.__recompile is True:
interfaces = self.__sol_compiler.compile()
else:
interfaces = self.__registrar.dump_chain()
interfaces = self._registrar.dump_chain()
# Setup the registrar and base contract factory cahche
self.__registrar = registrar
self.__raw_contract_cache = interfaces
class ProviderError(Exception):
pass
def get_contract_factory(self, contract_name):
"""Retrieve compiled interface data from the cache and return web3 contract"""
try:
interface = self.__raw_contract_cache[contract_name]
except KeyError:
raise self.ProviderError('{} is not a compiled contract.'.format(contract_name))
raise self.ContractInterfaceError('{} is not a compiled contract.'.format(contract_name))
contract = self.w3.eth.contract(abi=interface['abi'],
bytecode=interface['bin'],
@ -211,10 +210,26 @@ class ContractProvider:
def get_contract_address(self, contract_name: str) -> List[str]:
"""Retrieve all known addresses for this contract"""
contracts = self.__registrar.lookup_contract(contract_name=contract_name)
contracts = self._registrar.lookup_contract(contract_name=contract_name)
addresses = [c['addr'] for c in contracts]
return addresses
def get_contract(self, address: str) -> Contract:
"""Instantiate a deployed contract from registrar data"""
contract_data = self._registrar.dump_contract(address=address)
contract = self.w3.eth.contract(abi=contract_data['abi'], address=contract_data['addr'])
return contract
class DeployerInterface(ContractInterface):
def __init__(self, deployer_address:str=None, *args, **kwargs):
super().__init__(*args, **kwargs)
if deployer_address is None:
deployer_address = self.w3.eth.coinbase # coinbase / etherbase
self.deployer_address = deployer_address
def deploy_contract(self, contract_name: str, *args, **kwargs) -> Tuple[Contract, str]:
"""
Retrieve compiled interface data from the cache and
@ -246,14 +261,8 @@ class ContractProvider:
# Instantiate & enroll contract
#
contract = contract_factory(address=address)
self.__registrar.enroll(contract_name=contract_name,
contract_addr=contract.address,
contract_abi=contract_factory.abi)
self._registrar.enroll(contract_name=contract_name,
contract_addr=contract.address,
contract_abi=contract_factory.abi)
return contract, txhash
def get_contract(self, address: str) -> Contract:
"""Instantiate a deployed contract from registrar data"""
contract_data = self.__registrar.dump_contract(address=address)
contract = self.w3.eth.contract(abi=contract_data['abi'], address=contract_data['addr'])
return contract

View File

@ -21,7 +21,7 @@ class BlockchainArrangement:
self._rate = rate
self.value = value
self.lock_periods = lock_periods # TODO: datetime -> duration in blocks
self.lock_periods = lock_periods # TODO: <datetime> -> lock_periods
self.is_published = False

View File

@ -31,7 +31,7 @@ def main():
escrow.deploy()
# Creator deploys the policy manager
policy_manager, txhash = chain.provider.get_or_deploy_contract(
policy_manager, txhash = chain.interface.get_or_deploy_contract(
'PolicyManager', deploy_args=[escrow.contract.address],
deploy_transaction={'from': creator})
tx = escrow.transact({'from': creator}).setPolicyManager(policy_manager.address)

View File

@ -27,7 +27,7 @@ def main():
# alice = web3.eth.accounts[2]
# Create an ERC20 token
token, tx = chain.provider.get_or_deploy_contract(
token, tx = chain.interface.get_or_deploy_contract(
'HumanStandardToken', deploy_args=[
int(1e9) * M, int(1e10) * M, 'NuCypher', 6, 'NU'],
deploy_transaction={
@ -35,7 +35,7 @@ def main():
chain.wait.for_receipt(tx, timeout=TIMEOUT)
print("Deployed HumanStandardToken, tx hash is", tx)
escrow, tx = chain.provider.get_or_deploy_contract(
escrow, tx = chain.interface.get_or_deploy_contract(
'Escrow', deploy_args=[token.address] + MINING_COEFF,
deploy_transaction={'from': creator})
chain.wait.for_receipt(tx, timeout=TIMEOUT)

View File

@ -9,11 +9,13 @@ from typing import Dict, ClassVar
from typing import Union, List
from bytestring_splitter import BytestringSplitter
from nucypher.blockchain.eth.agents import PolicyAgent
from nucypher.network.node import NetworkyStuff
from umbral.keys import UmbralPublicKey
from constant_sorrow import constants, default_constant_splitter
from nucypher.blockchain.eth.actors import PolicyAuthor
from nucypher.blockchain.eth.actors import PolicyAuthor, Miner
from nucypher.config.configs import NucypherConfig
from nucypher.crypto.api import secure_random, keccak_digest, encrypt_and_sign
from nucypher.crypto.constants import PUBLIC_KEY_LENGTH
@ -38,7 +40,8 @@ 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.
@ -60,7 +63,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")
@ -319,17 +322,13 @@ class Character(object):
self.known_nodes.update(new_nodes)
class FakePolicyAgent: # TODO: #192
_token = "fake token"
class Alice(Character, PolicyAuthor):
_server_class = NucypherSeedOnlyDHTServer
_default_crypto_powerups = [SigningPower, EncryptingPower, DelegatingPower]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
PolicyAuthor.__init__(self, self.address, policy_agent=FakePolicyAgent())
PolicyAuthor.__init__(self, address=self.address, *args, **kwargs)
def generate_kfrags(self, bob, label, m, n) -> List:
"""
@ -597,17 +596,18 @@ class Bob(Character):
raise RuntimeError("Not verified - replace this with real message.")
class Ursula(Character, ProxyRESTServer):
class Ursula(Character, ProxyRESTServer, Miner):
_server_class = NucypherDHTServer
_alice_class = Alice
_default_crypto_powerups = [SigningPower, EncryptingPower]
def __init__(self, dht_port=None, ip_address=None, dht_ttl=0,
rest_port=None, db_name=None,
*args, **kwargs):
def __init__(self, dht_port=None, ip_address=None, dht_ttl=0, rest_port=None, db_name=None, *args, **kwargs):
self.dht_port = dht_port
self.dht_ttl = dht_ttl
self.ip_address = ip_address
self._work_orders = []
ProxyRESTServer.__init__(self, rest_port, db_name)
super().__init__(*args, **kwargs)

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,109 @@ class NetworkConfig:
return self.__db_path
class BlockchainConfig:
__default_providers = (IPCProvider(ipc_path='/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 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

@ -7,9 +7,13 @@ from nucypher.network.capabilities import ServerCapability
from umbral.fragments import CapsuleFrag
class NucypherNode(Node):
class NucypherDHTNode(Node):
def __init__(self, id, ip=None, port=None, capabilities=None, capabilities_as_strings=None, *args, **kwargs):
if capabilities_as_strings is None:
capabilities_as_strings = []
def __init__(self, id, ip=None, port=None, capabilities=None, capabilities_as_strings=[], *args, **kwargs):
self.id = id
self.ip = ip
self.port = port
@ -20,6 +24,8 @@ class NucypherNode(Node):
for capability_name in capabilities_as_strings:
self.capabilities.append(ServerCapability.from_name(capability_name))
super().__init__(id, ip, port, *args, **kwargs)
def can_store(self):
for c in self.capabilities:
if c.prohibits_storage:

View File

@ -7,7 +7,7 @@ from nucypher.crypto.api import keccak_digest
from nucypher.crypto.constants import PUBLIC_KEY_LENGTH, KECCAK_DIGEST_LENGTH
from nucypher.crypto.signing import Signature
from bytestring_splitter import BytestringSplitter
from nucypher.network.node import NucypherNode
from nucypher.network.node import NucypherDHTNode
from nucypher.network.routing import NucypherRoutingTable
from umbral.keys import UmbralPublicKey
@ -31,8 +31,8 @@ class NucypherHashProtocol(KademliaProtocol):
return True
def rpc_ping(self, sender, nodeid, node_capabilities=[]):
source = NucypherNode(nodeid, sender[0], sender[1],
capabilities_as_strings=node_capabilities)
source = NucypherDHTNode(nodeid, sender[0], sender[1],
capabilities_as_strings=node_capabilities)
self.welcomeIfNewNode(source)
return self.sourceNode.id
@ -72,7 +72,7 @@ class NucypherHashProtocol(KademliaProtocol):
return True
def rpc_store(self, sender, nodeid, key, value):
source = NucypherNode(nodeid, sender[0], sender[1])
source = NucypherDHTNode(nodeid, sender[0], sender[1])
self.welcomeIfNewNode(source)
self.log.debug("got a store request from %s" % str(sender))

View File

@ -17,7 +17,7 @@ from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.powers import EncryptingPower, SigningPower
from nucypher.keystore.threading import ThreadedSession
from nucypher.network.capabilities import SeedOnly, ServerCapability
from nucypher.network.node import NucypherNode
from nucypher.network.node import NucypherDHTNode
from nucypher.network.protocols import NucypherSeedOnlyProtocol, NucypherHashProtocol, \
dht_value_splitter, dht_with_hrac_splitter
from nucypher.network.storage import SeedOnlyStorage
@ -30,7 +30,7 @@ class NucypherDHTServer(Server):
def __init__(self, ksize=20, alpha=3, id=None, storage=None, *args, **kwargs):
super().__init__(ksize=20, alpha=3, id=None, storage=None, *args, **kwargs)
self.node = NucypherNode(id or digest(
self.node = NucypherDHTNode(id or digest(
random.getrandbits(255))) # TODO: Assume that this can be attacked to get closer to desired kFrags.
def serialize_capabilities(self):
@ -41,7 +41,7 @@ class NucypherDHTServer(Server):
Announce node including capabilities
"""
result = await self.protocol.ping(addr, self.node.id, self.serialize_capabilities())
return NucypherNode(result[1], addr[0], addr[1]) if result[0] else None
return NucypherDHTNode(result[1], addr[0], addr[1]) if result[0] else None
async def set_digest(self, dkey, value):
"""

View File

@ -232,11 +232,9 @@ class Policy(object):
self.treasure_map.add_ursula(arrangement.ursula)
def make_arrangement(self, deposit, expiration):
return Arrangement(self.alice, self.hrac(), expiration=expiration,
deposit=deposit)
return Arrangement(self.alice, self.hrac(), expiration=expiration, deposit=deposit)
def find_ursulas(self, networky_stuff, deposit, expiration,
num_ursulas=None):
def find_ursulas(self, networky_stuff, deposit, expiration, num_ursulas=None):
"""
:param networky_stuff: A compliant interface (maybe a Client instance) to be used to engage the DHT swarm.
"""

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

@ -24,11 +24,11 @@ def escrow(web3, chain):
node3 = web3.eth.accounts[3]
# Creator deploys the escrow
escrow_library, _ = chain.provider.deploy_contract(
escrow_library, _ = chain.interface.deploy_contract(
'MinersEscrowV1Mock', [node1, node2, node3], [1, 2, 3]
)
escrow_dispatcher, _ = chain.provider.deploy_contract(
escrow_dispatcher, _ = chain.interface.deploy_contract(
'Dispatcher', escrow_library.address
)
contract = web3.eth.contract(
@ -41,8 +41,8 @@ def escrow(web3, chain):
@pytest.fixture()
def policy_manager(web3, chain):
# Creator deploys the escrow
contract, _ = chain.provider.deploy_contract('PolicyManagerV1Mock')
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract.address)
contract, _ = chain.interface.deploy_contract('PolicyManagerV1Mock')
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract.address)
contract = web3.eth.contract(
abi=contract.abi,
address=dispatcher.address,
@ -55,10 +55,10 @@ def test_voting(web3, chain, escrow, policy_manager):
creator, node1, node2, node3, *everyone_else = web3.eth.accounts
# Deploy contract
government_library, _ = chain.provider.deploy_contract(
government_library, _ = chain.interface.deploy_contract(
'Government', escrow.address, policy_manager.address, 1,
)
government_dispatcher, _ = chain.provider.deploy_contract(
government_dispatcher, _ = chain.interface.deploy_contract(
'Dispatcher', government_library.address
)
government = web3.eth.contract(
@ -84,7 +84,7 @@ def test_voting(web3, chain, escrow, policy_manager):
chain.wait_for_receipt(tx)
# Deploy second version of the government contract
government_library_v2, _ = chain.provider.deploy_contract(
government_library_v2, _ = chain.interface.deploy_contract(
'Government', escrow.address, policy_manager.address, 1,
)
assert government_library.address != government_library_v2.address
@ -231,10 +231,10 @@ def test_upgrade(web3, chain, escrow, policy_manager):
node1 = web3.eth.accounts[1]
# Deploy contract
government_library_v1, _ = chain.provider.deploy_contract(
government_library_v1, _ = chain.interface.deploy_contract(
'Government', escrow.address, policy_manager.address, 1,
)
government_dispatcher, _ = chain.provider.deploy_contract(
government_dispatcher, _ = chain.interface.deploy_contract(
'Dispatcher', government_library_v1.address,
)
government = web3.eth.contract(
@ -247,20 +247,20 @@ def test_upgrade(web3, chain, escrow, policy_manager):
upgrade_committed_log = government.events.UpgradeCommitted.createFilter(fromBlock='latest')
# Deploy second version of the government contract
government_library_v2, _ = chain.provider.deploy_contract(
government_library_v2, _ = chain.interface.deploy_contract(
'Government', escrow.address, policy_manager.address, 1,
)
# Get first version of the escrow contract
escrow_library_v1 = escrow.functions.target().call()
# Deploy second version of the escrow contract
escrow_library_v2, _ = chain.provider.deploy_contract(
escrow_library_v2, _ = chain.interface.deploy_contract(
'MinersEscrowV1Mock', [node1], [1]
)
escrow_library_v2 = escrow_library_v2.address
# Get first version of the policy manager contract
policy_manager_library_v1 = policy_manager.functions.target().call()
# Deploy second version of the policy manager contract
policy_manager_library_v2, _ = chain.provider.deploy_contract('PolicyManagerV1Mock')
policy_manager_library_v2, _ = chain.interface.deploy_contract('PolicyManagerV1Mock')
policy_manager_library_v2 = policy_manager_library_v2.address
# Transfer ownership
@ -450,15 +450,16 @@ def test_upgrade(web3, chain, escrow, policy_manager):
assert event_args['successful']
@pytest.mark.slow()
def test_cancel_upgrading(web3, chain, escrow, policy_manager):
creator = web3.eth.accounts[0]
node1 = web3.eth.accounts[1]
# Deploy contract
government_library, _ = chain.provider.deploy_contract(
government_library, _ = chain.interface.deploy_contract(
'GovernmentV2Mock', escrow.address, policy_manager.address, 1,
)
government_dispatcher, _ = chain.provider.deploy_contract(
government_dispatcher, _ = chain.interface.deploy_contract(
'Dispatcher', government_library.address
)
government = web3.eth.contract(
@ -468,7 +469,7 @@ def test_cancel_upgrading(web3, chain, escrow, policy_manager):
)
escrow_library = escrow.functions.target().call()
policy_manager_library = policy_manager.functions.target().call()
upgradeable_bad, _ = chain.provider.deploy_contract('UpgradeableBad')
upgradeable_bad, _ = chain.interface.deploy_contract('UpgradeableBad')
upgrade_committed_log = government.events.UpgradeCommitted.createFilter(fromBlock=0)
tx = government.functions.transferOwnership(government.address).transact({'from': creator})
chain.wait_for_receipt(tx)
@ -610,15 +611,15 @@ def test_verifying_state(web3, chain):
address2 = web3.eth.accounts[2]
# Deploy contract
government_library_v1, _ = chain.provider.deploy_contract(
government_library_v1, _ = chain.interface.deploy_contract(
'Government', address1, address2, 1,
)
government_dispatcher, _ = chain.provider.deploy_contract(
government_dispatcher, _ = chain.interface.deploy_contract(
'Dispatcher', government_library_v1.address
)
# Deploy second version of the government contract
government_library_v2, _ = chain.provider.deploy_contract(
government_library_v2, _ = chain.interface.deploy_contract(
'GovernmentV2Mock', address2, address1, 2,
)
government = web3.eth.contract(
@ -638,7 +639,7 @@ def test_verifying_state(web3, chain):
assert 3 == government.functions.valueToCheck().call()
# Can't upgrade to the previous version or to the bad version
government_library_bad, _ = chain.provider.deploy_contract('GovernmentBad')
government_library_bad, _ = chain.interface.deploy_contract('GovernmentBad')
with pytest.raises((TransactionFailed, ValueError)):
tx = government_dispatcher.functions.upgrade(government_library_v1.address).transact({'from': creator})
chain.wait_for_receipt(tx)

View File

@ -40,14 +40,14 @@ ROLLBACK_POLICY_MANAGER = 5
@pytest.fixture()
def token(chain):
# Create an ERC20 token
contract, _ = chain.provider.deploy_contract('NuCypherToken', 2 * 10 ** 9)
contract, _ = chain.interface.deploy_contract('NuCypherToken', 2 * 10 ** 9)
return contract
@pytest.fixture()
def escrow(web3, chain, token):
# Creator deploys the escrow
contract, _ = chain.provider.deploy_contract(
contract, _ = chain.interface.deploy_contract(
'MinersEscrow',
token.address,
1,
@ -58,7 +58,7 @@ def escrow(web3, chain, token):
100,
2000)
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract.address)
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract.address)
# Wrap dispatcher contract
contract = web3.eth.contract(abi=contract.abi, address=dispatcher.address, ContractFactoryClass=Contract)
@ -70,8 +70,8 @@ def policy_manager(web3, chain, escrow):
creator = web3.eth.accounts[0]
# Creator deploys the policy manager
contract, _ = chain.provider.deploy_contract('PolicyManager', escrow.address)
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract.address)
contract, _ = chain.interface.deploy_contract('PolicyManager', escrow.address)
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract.address)
# Wrap dispatcher contract
contract = web3.eth.contract(abi=contract.abi, address=dispatcher.address, ContractFactoryClass=Contract)
@ -87,8 +87,8 @@ def government(web3, chain, escrow, policy_manager):
creator = web3.eth.accounts[0]
# Creator deploys the government
contract, _ = chain.provider.deploy_contract('Government', escrow.address, policy_manager.address, 1)
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract.address)
contract, _ = chain.interface.deploy_contract('Government', escrow.address, policy_manager.address, 1)
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract.address)
# Wrap dispatcher contract
contract = web3.eth.contract(abi=contract.abi, address=dispatcher.address, ContractFactoryClass=Contract)
@ -144,14 +144,16 @@ def test_all(web3, chain, token, escrow, policy_manager, government):
chain.wait_for_receipt(tx)
# Deposit some tokens to the user escrow and lock them
user_escrow_1, _ = chain.provider.deploy_contract('UserEscrow', token.address, escrow.address, policy_manager.address, government.address)
user_escrow_1, _ = chain.interface.deploy_contract('UserEscrow', token.address, escrow.address, policy_manager.address, government.address)
tx = user_escrow_1.functions.transferOwnership(ursula3).transact({'from': creator})
chain.wait_for_receipt(tx)
tx = token.functions.approve(user_escrow_1.address, 10000).transact({'from': creator})
chain.wait_for_receipt(tx)
tx = user_escrow_1.functions.initialDeposit(10000, 20 * 60 * 60).transact({'from': creator})
chain.wait_for_receipt(tx)
user_escrow_2, _ = chain.provider.deploy_contract('UserEscrow', token.address, escrow.address, policy_manager.address, government.address)
user_escrow_2, _ = chain.interface.deploy_contract('UserEscrow', token.address, escrow.address, policy_manager.address, government.address)
tx = user_escrow_2.functions.transferOwnership(ursula4).transact({'from': creator})
chain.wait_for_receipt(tx)
tx = token.functions.approve(user_escrow_2.address, 10000).transact({'from': creator})
@ -403,7 +405,7 @@ def test_all(web3, chain, token, escrow, policy_manager, government):
policy_manager_v1 = policy_manager.functions.target().call()
government_v1 = government.functions.target().call()
# Creator deploys the contracts as the second versions
escrow_v2, _ = chain.provider.deploy_contract(
escrow_v2, _ = chain.interface.deploy_contract(
'MinersEscrow',
token.address,
1,
@ -413,8 +415,8 @@ def test_all(web3, chain, token, escrow, policy_manager, government):
2,
100,
2000)
policy_manager_v2, _ = chain.provider.deploy_contract('PolicyManager', escrow.address)
government_v2, _ = chain.provider.deploy_contract('Government', escrow.address, policy_manager.address, 1)
policy_manager_v2, _ = chain.interface.deploy_contract('PolicyManager', escrow.address)
government_v2, _ = chain.interface.deploy_contract('Government', escrow.address, policy_manager.address, 1)
assert FINISHED_STATE == government.functions.getVotingState().call()
# Alice can't create voting

View File

@ -6,7 +6,7 @@ from eth_tester.exceptions import TransactionFailed
@pytest.fixture()
def token(chain):
# Create an ERC20 token
token, _ = chain.provider.deploy_contract('NuCypherToken', 2 * 10 ** 40)
token, _ = chain.interface.deploy_contract('NuCypherToken', 2 * 10 ** 40)
return token
@pytest.mark.slow
@ -15,7 +15,7 @@ def test_issuer(web3, chain, token):
ursula = web3.eth.accounts[1]
# Creator deploys the issuer
issuer, _ = chain.provider.deploy_contract(
issuer, _ = chain.interface.deploy_contract(
'IssuerMock', token.address, 1, 10 ** 46, int(1e7), int(1e7)
)
@ -68,7 +68,7 @@ def test_inflation_rate(web3, chain, token):
ursula = web3.eth.accounts[1]
# Creator deploys the miner
issuer, _ = chain.provider.deploy_contract(
issuer, _ = chain.interface.deploy_contract(
'IssuerMock', token.address, 1, 2 * 10 ** 19, 1, 1
)
@ -111,13 +111,13 @@ def test_verifying_state(web3, chain, token):
creator = web3.eth.accounts[0]
# Deploy contract
contract_library_v1, _ = chain.provider.deploy_contract(
contract_library_v1, _ = chain.interface.deploy_contract(
'Issuer', token.address, 1, 1, 1, 1
)
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract_library_v1.address)
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract_library_v1.address)
# Deploy second version of the contract
contract_library_v2, _ = chain.provider.deploy_contract('IssuerV2Mock', token.address, 2, 2, 2, 2)
contract_library_v2, _ = chain.interface.deploy_contract('IssuerV2Mock', token.address, 2, 2, 2, 2)
contract = web3.eth.contract(
abi=contract_library_v2.abi,
address=dispatcher.address,
@ -146,7 +146,7 @@ def test_verifying_state(web3, chain, token):
assert 3 == contract.functions.valueToCheck().call()
# Can't upgrade to the previous version or to the bad version
contract_library_bad, _ = chain.provider.deploy_contract('IssuerBad', token.address, 2, 2, 2, 2)
contract_library_bad, _ = chain.interface.deploy_contract('IssuerBad', token.address, 2, 2, 2, 2)
with pytest.raises((TransactionFailed, ValueError)):
tx = dispatcher.functions.upgrade(contract_library_v1.address).transact({'from': creator})
chain.wait_for_receipt(tx)

View File

@ -14,7 +14,7 @@ LAST_ACTIVE_PERIOD_FIELD = 4
@pytest.fixture()
def token(chain):
# Create an ERC20 token
token, _ = chain.provider.deploy_contract('NuCypherToken', 2 * 10 ** 9)
token, _ = chain.interface.deploy_contract('NuCypherToken', 2 * 10 ** 9)
return token
@ -22,11 +22,11 @@ def token(chain):
def escrow_contract(web3, chain, token, request):
def make_escrow(max_allowed_locked_tokens):
# Creator deploys the escrow
contract, _ = chain.provider.deploy_contract(
contract, _ = chain.interface.deploy_contract(
'MinersEscrow', token.address, 1, 4 * 2 * 10 ** 7, 4, 4, 2, 100, max_allowed_locked_tokens)
if request.param:
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract.address)
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract.address)
contract = web3.eth.contract(
abi=contract.abi,
address=dispatcher.address,
@ -48,7 +48,7 @@ def test_escrow(web3, chain, token, escrow_contract):
divides_log = escrow.events.Divided.createFilter(fromBlock='latest')
withdraw_log = escrow.events.Withdrawn.createFilter(fromBlock='latest')
policy_manager, _ = chain.provider.deploy_contract(
policy_manager, _ = chain.interface.deploy_contract(
'PolicyManagerForMinersEscrowMock', token.address, escrow.address
)
tx = escrow.functions.setPolicyManager(policy_manager.address).transact()
@ -498,7 +498,7 @@ def test_mining(web3, chain, token, escrow_contract):
tx = escrow.functions.initialize().transact({'from': creator})
chain.wait_for_receipt(tx)
policy_manager, _ = chain.provider.deploy_contract(
policy_manager, _ = chain.interface.deploy_contract(
'PolicyManagerForMinersEscrowMock', token.address, escrow.address
)
tx = escrow.functions.setPolicyManager(policy_manager.address).transact({'from': creator})
@ -828,13 +828,13 @@ def test_verifying_state(web3, chain, token):
miner = web3.eth.accounts[1]
# Deploy contract
contract_library_v1, _ = chain.provider.deploy_contract(
contract_library_v1, _ = chain.interface.deploy_contract(
'MinersEscrow', token.address, 1, int(8e7), 4, 4, 2, 100, 1500
)
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract_library_v1.address)
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract_library_v1.address)
# Deploy second version of the contract
contract_library_v2, _ = chain.provider.deploy_contract(
contract_library_v2, _ = chain.interface.deploy_contract(
'MinersEscrowV2Mock', token.address, 2, 2, 2, 2, 2, 2, 2, 2
)
@ -845,7 +845,7 @@ def test_verifying_state(web3, chain, token):
assert 1500 == contract.functions.maxAllowableLockedTokens().call()
# Initialize contract and miner
policy_manager, _ = chain.provider.deploy_contract('PolicyManagerForMinersEscrowMock', token.address, contract.address)
policy_manager, _ = chain.interface.deploy_contract('PolicyManagerForMinersEscrowMock', token.address, contract.address)
tx = contract.functions.setPolicyManager(policy_manager.address).transact({'from': creator})
chain.wait_for_receipt(tx)
@ -872,7 +872,7 @@ def test_verifying_state(web3, chain, token):
assert 3 == contract.functions.valueToCheck().call()
# Can't upgrade to the previous version or to the bad version
contract_library_bad, _ = chain.provider.deploy_contract(
contract_library_bad, _ = chain.interface.deploy_contract(
'MinersEscrowBad', token.address, 2, 2, 2, 2, 2, 2, 2
)

View File

@ -23,7 +23,7 @@ def escrow(web3, chain):
node2 = web3.eth.accounts[4]
node3 = web3.eth.accounts[5]
# Creator deploys the escrow
escrow, _ = chain.provider.deploy_contract('MinersEscrowForPolicyMock', [node1, node2, node3], 1)
escrow, _ = chain.interface.deploy_contract('MinersEscrowForPolicyMock', [node1, node2, node3], 1)
return escrow
@ -33,14 +33,14 @@ def policy_manager(web3, chain, escrow, request):
client = web3.eth.accounts[1]
# Creator deploys the policy manager
contract, _ = chain.provider.deploy_contract('PolicyManager', escrow.address)
contract, _ = chain.interface.deploy_contract('PolicyManager', escrow.address)
# Give client some ether
tx = web3.eth.sendTransaction({'from': web3.eth.coinbase, 'to': client, 'value': 10000})
chain.wait_for_receipt(tx)
if request.param:
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract.address)
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract.address)
# Deploy second version of the government contract
contract = web3.eth.contract(
@ -679,11 +679,11 @@ def test_verifying_state(web3, chain):
address2 = web3.eth.accounts[2]
# Deploy contract
contract_library_v1, _ = chain.provider.deploy_contract('PolicyManager', address1)
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract_library_v1.address)
contract_library_v1, _ = chain.interface.deploy_contract('PolicyManager', address1)
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract_library_v1.address)
# Deploy second version of the contract
contract_library_v2, _ = chain.provider.deploy_contract('PolicyManagerV2Mock', address2)
contract_library_v2, _ = chain.interface.deploy_contract('PolicyManagerV2Mock', address2)
contract = web3.eth.contract(
abi=contract_library_v2.abi,
address=dispatcher.address,
@ -700,7 +700,7 @@ def test_verifying_state(web3, chain):
assert 3 == contract.functions.valueToCheck().call()
# Can't upgrade to the previous version or to the bad version
contract_library_bad, _ = chain.provider.deploy_contract('PolicyManagerBad', address2)
contract_library_bad, _ = chain.interface.deploy_contract('PolicyManagerBad', address2)
with pytest.raises((TransactionFailed, ValueError)):
tx = dispatcher.functions.upgrade(contract_library_v1.address).transact({'from': creator})
chain.wait_for_receipt(tx)

View File

@ -14,7 +14,7 @@ def test_create_token(web3, chain):
account2 = web3.eth.accounts[2]
# Create an ERC20 token
token, txhash = chain.provider.deploy_contract('NuCypherToken', 10 ** 9)
token, txhash = chain.interface.deploy_contract('NuCypherToken', 10 ** 9)
assert txhash is not None
# Account balances
@ -58,8 +58,8 @@ def test_approve_and_call(web3, chain):
account1 = web3.eth.accounts[1]
account2 = web3.eth.accounts[2]
token, _ = chain.provider.deploy_contract('NuCypherToken', 10 ** 9)
mock, _ = chain.provider.deploy_contract('ReceiveApprovalMethodMock')
token, _ = chain.interface.deploy_contract('NuCypherToken', 10 ** 9)
mock, _ = chain.interface.deploy_contract('ReceiveApprovalMethodMock')
tx = token.functions.approve(account1, 100).transact({'from': creator})
chain.wait_for_receipt(tx)

View File

@ -5,7 +5,7 @@ from eth_tester.exceptions import TransactionFailed
@pytest.fixture()
def token(chain):
# Create an ERC20 token
token, _ = chain.provider.deploy_contract('NuCypherToken', int(2e9))
token, _ = chain.interface.deploy_contract('NuCypherToken', int(2e9))
return token
@ -13,7 +13,7 @@ def token(chain):
def escrow(web3, chain, token):
creator = web3.eth.accounts[0]
# Creator deploys the escrow
contract, _ = chain.provider.deploy_contract('MinersEscrowForUserEscrowMock', token.address)
contract, _ = chain.interface.deploy_contract('MinersEscrowForUserEscrowMock', token.address)
# Give escrow some coins
tx = token.functions.transfer(contract.address, 10000).transact({'from': creator})
@ -24,13 +24,13 @@ def escrow(web3, chain, token):
@pytest.fixture()
def policy_manager(chain):
contract, _ = chain.provider.deploy_contract('PolicyManagerForUserEscrowMock')
contract, _ = chain.interface.deploy_contract('PolicyManagerForUserEscrowMock')
return contract
@pytest.fixture()
def government(chain):
contract, _ = chain.provider.deploy_contract('GovernmentForUserEscrowMock')
contract, _ = chain.interface.deploy_contract('GovernmentForUserEscrowMock')
return contract
@ -40,7 +40,7 @@ def user_escrow(web3, chain, token, escrow, policy_manager, government):
user = web3.eth.accounts[1]
# Creator deploys the user escrow
contract, _ = chain.provider.deploy_contract(
contract, _ = chain.interface.deploy_contract(
'UserEscrow', token.address, escrow.address, policy_manager.address, government.address)
# Transfer ownership

View File

@ -2,98 +2,99 @@ import os
import pytest
from nucypher.blockchain.eth.actors import Miner
from nucypher.blockchain.eth.agents import MinerAgent
from nucypher.blockchain.eth.actors import Miner, PolicyAuthor
@pytest.fixture(scope='module')
def miner(chain, mock_token_agent, mock_miner_agent):
mock_token_agent.token_airdrop(amount=100000 * mock_token_agent._M)
_origin, ursula, *everybody_else = chain.provider.w3.eth.accounts
miner = Miner(miner_agent=mock_miner_agent, address=ursula)
return miner
class TestMiner:
@pytest.fixture(scope='class')
def miner(self, chain, mock_token_agent, mock_miner_agent):
mock_token_agent.token_airdrop(amount=100000 * mock_token_agent.M)
_origin, ursula, *everybody_else = chain.interface.w3.eth.accounts
miner = Miner(miner_agent=mock_miner_agent, address=ursula)
return miner
def test_miner_locking_tokens(chain, miner, mock_miner_agent):
def test_miner_locking_tokens(self, chain, miner, mock_miner_agent):
assert mock_miner_agent.min_allowed_locked < miner.token_balance(), "Insufficient miner balance"
assert mock_miner_agent.min_allowed_locked < miner.token_balance(), "Insufficient miner balance"
miner.stake(amount=mock_miner_agent.min_allowed_locked, # Lock the minimum amount of tokens
lock_periods=mock_miner_agent.min_locked_periods) # ... for the fewest number of periods
miner.stake(amount=mock_miner_agent.min_allowed_locked, # Lock the minimum amount of tokens
lock_periods=mock_miner_agent.min_locked_periods) # ... for the fewest number of periods
# Verify that the escrow is "approved" to receive tokens
assert mock_miner_agent.token_agent.contract.functions.allowance(miner.address, mock_miner_agent.contract_address).call() == 0
# Verify that the escrow is "approved" to receive tokens
assert mock_miner_agent.token_agent.contract.functions.allowance(miner.address, mock_miner_agent.contract_address).call() == 0
# Staking starts after one period
assert mock_miner_agent.contract.functions.getLockedTokens(miner.address).call() == 0
# Staking starts after one period
assert mock_miner_agent.contract.functions.getLockedTokens(miner.address).call() == 0
# Wait for it...
chain.time_travel(periods=1)
assert mock_miner_agent.contract.functions.getLockedTokens(miner.address).call() == mock_miner_agent.min_allowed_locked
def test_miner_collects_staking_reward_tokens(chain, miner, mock_token_agent, mock_miner_agent, mock_policy_agent):
# Capture the current token balance of the miner
initial_balance = miner.token_balance()
assert mock_token_agent.get_balance(miner.address) == miner.token_balance()
# Have other address lock tokens
_origin, *everybody_else = chain.provider.w3.eth.accounts
mock_miner_agent.spawn_random_miners(addresses=everybody_else)
# ...wait out the lock period...
for _ in range(28):
# Wait for it...
chain.time_travel(periods=1)
miner.confirm_activity()
assert mock_miner_agent.contract.functions.getLockedTokens(miner.address).call() == mock_miner_agent.min_allowed_locked
# ...wait more...
chain.time_travel(periods=2)
miner.mint()
miner.collect_staking_reward()
@pytest.mark.slow()
@pytest.mark.usefixtures("mock_policy_agent")
def test_miner_collects_staking_reward_tokens(self, chain, miner, mock_token_agent, mock_miner_agent):
final_balance = mock_token_agent.get_balance(miner.address)
assert final_balance > initial_balance
# Capture the current token balance of the miner
initial_balance = miner.token_balance()
assert mock_token_agent.get_balance(miner.address) == miner.token_balance()
miner.stake(amount=mock_miner_agent.min_allowed_locked, # Lock the minimum amount of tokens
lock_periods=mock_miner_agent.min_locked_periods) # ... for the fewest number of periods
# Have other address lock tokens
_origin, ursula, *everybody_else = chain.interface.w3.eth.accounts
mock_miner_agent.spawn_random_miners(addresses=everybody_else)
# ...wait out the lock period...
for _ in range(28):
chain.time_travel(periods=1)
miner.confirm_activity()
# ...wait more...
chain.time_travel(periods=2)
miner.mint()
miner.collect_staking_reward()
final_balance = mock_token_agent.get_balance(miner.address)
assert final_balance > initial_balance
def test_publish_miner_datastore(self, miner):
# Publish Miner IDs to the DHT
some_data = os.urandom(32)
_txhash = miner.publish_data(some_data)
# Fetch the miner Ids
stored_miner_ids = miner.fetch_data()
assert len(stored_miner_ids) == 1
assert some_data == stored_miner_ids[0]
# Repeat, with another miner ID
another_mock_miner_id = os.urandom(32)
_txhash = miner.publish_data(another_mock_miner_id)
stored_miner_ids = miner.fetch_data()
assert len(stored_miner_ids) == 2
assert another_mock_miner_id == stored_miner_ids[1]
supposedly_the_same_miner_id = miner.miner_agent.contract.functions.getMinerId(miner.address, 1).call()
assert another_mock_miner_id == supposedly_the_same_miner_id
@pytest.mark.slow()
def test_sample_miners(chain, mock_miner_agent, mock_token_agent):
mock_token_agent.token_airdrop(amount=100000 * mock_token_agent._M)
class TestPolicyAuthor:
# Have other address lock tokens
_origin, ursula, *everybody_else = chain.provider.w3.eth.accounts
mock_miner_agent.spawn_random_miners(addresses=everybody_else)
@pytest.fixture(scope='class')
def author(self, chain, mock_token_agent, mock_policy_agent):
mock_token_agent.ether_airdrop(amount=100000 * mock_token_agent.M)
_origin, ursula, alice, *everybody_else = chain.interface.w3.eth.accounts
miner = PolicyAuthor(address=alice, policy_agent=mock_policy_agent)
return miner
chain.time_travel(periods=1)
def test_create_policy_author(self, chain, mock_policy_agent):
_origin, ursula, alice, *everybody_else = chain.interface.w3.eth.accounts
with pytest.raises(MinerAgent.NotEnoughUrsulas):
mock_miner_agent.sample(quantity=100) # Waay more than we have deployed
miners = mock_miner_agent.sample(quantity=3)
assert len(miners) == 3
assert len(set(miners)) == 3
def test_publish_miner_datastore(miner):
# Publish Miner IDs to the DHT
some_data = os.urandom(32)
_txhash = miner.publish_data(some_data)
# Fetch the miner Ids
stored_miner_ids = miner.fetch_data()
assert len(stored_miner_ids) == 1
assert some_data == stored_miner_ids[0]
# Repeat, with another miner ID
another_mock_miner_id = os.urandom(32)
_txhash = miner.publish_data(another_mock_miner_id)
stored_miner_ids = miner.fetch_data()
assert len(stored_miner_ids) == 2
assert another_mock_miner_id == stored_miner_ids[1]
supposedly_the_same_miner_id = miner.miner_agent.contract.functions.getMinerId(miner.address, 1).call()
assert another_mock_miner_id == supposedly_the_same_miner_id
policy_author = PolicyAuthor(policy_agent=mock_policy_agent, address=alice)
assert policy_author.address == alice

View File

@ -1,14 +1,15 @@
import pytest
from nucypher.blockchain.eth.agents import MinerAgent
M = 10 ** 6
@pytest.mark.slow()
def test_get_swarm(chain, mock_token_agent, mock_miner_agent):
mock_token_agent.token_airdrop(amount=100000 * mock_token_agent._M)
mock_token_agent.token_airdrop(amount=100000 * mock_token_agent.M)
creator, *addresses = chain.provider.w3.eth.accounts
creator, *addresses = chain.interface.w3.eth.accounts
mock_miner_agent.spawn_random_miners(addresses=addresses)
@ -28,3 +29,20 @@ def test_get_swarm(chain, mock_token_agent, mock_miner_agent):
except ValueError:
pytest.fail()
@pytest.mark.slow()
def test_sample_miners(chain, mock_miner_agent, mock_token_agent):
mock_token_agent.token_airdrop(amount=100000 * mock_token_agent.M)
# Have other address lock tokens
_origin, ursula, *everybody_else = chain.interface.w3.eth.accounts
mock_miner_agent.spawn_random_miners(addresses=everybody_else)
chain.time_travel(periods=1)
with pytest.raises(MinerAgent.NotEnoughUrsulas):
mock_miner_agent.sample(quantity=100) # Waay more than we have deployed
miners = mock_miner_agent.sample(quantity=3)
assert len(miners) == 3
assert len(set(miners)) == 3

View File

@ -3,15 +3,13 @@ from nucypher.blockchain.eth.deployers import NucypherTokenDeployer
def test_chain_creation(chain):
# Ensure we are testing on the correct network...
assert chain._network == 'tester'
assert chain.config.network == 'tester'
# ... and that there are already some blocks mined
assert chain.provider.w3.eth.blockNumber >= 0
assert chain.interface.w3.eth.blockNumber >= 0
def test_nucypher_contract_compiled(chain):
# Check that populus paths are set...
# Ensure that solidity smart contacts are available, post-compile.
token_contract_identifier = NucypherTokenDeployer(blockchain=chain)._contract_name
assert token_contract_identifier in chain.provider._ContractProvider__raw_contract_cache
assert token_contract_identifier in chain.interface._ContractInterface__raw_contract_cache

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
@ -24,7 +24,7 @@ class MockTokenAgent(NucypherTokenAgent):
"""Airdrops tokens from creator address to all other addresses!"""
if addresses is None:
_creator, *addresses = self.blockchain.provider.w3.eth.accounts
_creator, *addresses = self.blockchain.interface.w3.eth.accounts
def txs():
for address in addresses:
@ -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

@ -12,12 +12,11 @@ from umbral.keys import UmbralPublicKey
import maya
def test_grant(alice, bob):
def test_grant(alice, bob, nucypher_test_config):
policy_end_datetime = maya.now() + datetime.timedelta(days=5)
n = 5
uri = b"this_is_the_path_to_which_access_is_being_granted"
policy = alice.grant(bob, uri, m=3, n=n,
expiration=policy_end_datetime)
policy = alice.grant(bob, uri, m=3, n=n, expiration=policy_end_datetime)
# The number of policies is equal to the number of Ursulas we're using (n)
assert len(policy._accepted_arrangements) == n

View File

@ -49,7 +49,7 @@ def test_actor_with_signing_power_can_sign():
assert verification is True
def test_anybody_can_verify():
def test_anybody_can_verify(nucypher_test_config, mock_policy_agent):
"""
In the last example, we used the lower-level Crypto API to verify the signature.
@ -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."
@ -71,6 +71,7 @@ def test_anybody_can_verify():
assert verification is True
assert cleartext is constants.NO_DECRYPTION_PERFORMED
"""
Chapter 2: ENCRYPTION
"""
@ -90,10 +91,12 @@ def test_anybody_can_encrypt():
assert signature == constants.NOT_SIGNED
assert ciphertext is not None
"""
What follows are various combinations of signing and encrypting, to match real-world scenarios.
"""
def test_sign_cleartext_and_encrypt(alice, bob):
"""
Exhibit One: Alice signs the cleartext and encrypts her signature inside the ciphertext.
@ -140,7 +143,6 @@ def test_encrypt_and_sign_including_signature_in_both_places(alice, bob):
assert cleartext == message
def test_encrypt_but_do_not_sign(alice, bob):
"""
Finally, Alice encrypts but declines to 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,21 @@
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 os.path import abspath, dirname
from web3 import EthereumTesterProvider, IPCProvider
from web3.middleware import geth_poa_middleware
from nucypher.blockchain.eth.chains import TheBlockchain, TesterBlockchain
from nucypher.blockchain.eth.deployers import PolicyManagerDeployer
from nucypher.blockchain.eth.interfaces import Registrar, ContractProvider
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
@ -31,7 +31,7 @@ def manual_geth_ipc_provider():
Provider backend
https:// github.com/ethereum/eth-tester
"""
ipc_provider = IPCProvider(ipc_path=os.path.join('/tmp/geth.ipc'))
ipc_provider = IPCProvider(ipc_path='/tmp/geth.ipc')
yield ipc_provider
@ -98,12 +98,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 +125,53 @@ 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, poa=False):
"""Compatibility fixture"""
if poa is True:
w3 = deployer_interface.interface.w3
w3.middleware_stack.inject(geth_poa_middleware, layer=0)
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 #
#
@ -170,7 +179,8 @@ def chain(contract_provider, airdrop=False):
@pytest.fixture(scope='module')
def mock_token_deployer(chain):
token_deployer = MockNucypherTokenDeployer(blockchain=chain)
origin, *everyone = chain.interface.w3.eth.coinbase
token_deployer = MockNucypherTokenDeployer(blockchain=chain, deployer_address=origin)
token_deployer.arm()
token_deployer.deploy()
yield token_deployer
@ -194,28 +204,26 @@ def mock_policy_manager_deployer(mock_miner_agent):
#
# Agents #
# Unused args preserve fixture dependency order #
#
@pytest.fixture(scope='module')
def mock_token_agent(mock_token_deployer):
token_agent = mock_token_deployer.make_agent()
assert mock_token_deployer._contract.address == token_agent.contract_address
assert mock_token_deployer.contract.address == token_agent.contract_address
yield token_agent
@pytest.fixture(scope='module')
@pytest.mark.usefixtures("mock_token_agent")
def mock_miner_agent(mock_miner_escrow_deployer):
miner_agent = mock_miner_escrow_deployer.make_agent()
assert mock_miner_escrow_deployer._contract.address == miner_agent.contract_address
assert mock_miner_escrow_deployer.contract.address == miner_agent.contract_address
yield miner_agent
@pytest.fixture(scope='module')
@pytest.mark.usefixtures("mock_miner_agent")
def mock_policy_agent(mock_policy_manager_deployer):
policy_agent = mock_policy_manager_deployer.make_agent()
assert mock_policy_manager_deployer._contract.address == policy_agent.contract_address
assert mock_policy_manager_deployer.contract.address == policy_agent.contract_address
yield policy_agent

View File

@ -7,11 +7,15 @@ 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.config.configs import NucypherConfig
from nucypher.crypto.signature import SignatureStamp
from nucypher.keystore import keystore
from nucypher.keystore.db import Base
from nucypher.crypto.signing import SignatureStamp
from nucypher.data_sources import DataSource
from nucypher.keystore import keystore
from nucypher.keystore.db import Base
@ -21,6 +25,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 +68,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 +84,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):